diff --git a/config/ruff.toml b/config/ruff.toml index 7277e2d6f..462ddcbce 100644 --- a/config/ruff.toml +++ b/config/ruff.toml @@ -35,27 +35,27 @@ ignore = [ "TRY003", # Avoid specifying long messages outside the exception class ] -logger-objects = ["griffelib.logger"] +logger-objects = ["griffe.logger", "griffelib.logger"] [lint.per-file-ignores] -"packages/griffe/src/griffe/__main__.py" = [ +"packages/griffecli/src/griffecli/__main__.py" = [ "D100", # Missing module docstring ] -"packages/griffe/src/griffe/_internal/cli.py" = [ +"packages/griffecli/src/griffecli/_internal/cli.py" = [ "T201", # Print statement ] -"packages/griffe/src/griffe/_internal/git.py" = [ +"packages/griffelib/src/griffelib/_internal/git.py" = [ "S603", # `subprocess` call: check for execution of untrusted input "S607", # Starting a process with a partial executable path ] -"packages/griffe/src/griffe/_internal/agents/nodes/*.py" = [ +"packages/griffelib/src/griffelib/_internal/agents/nodes/*.py" = [ "ARG001", # Unused function argument "N812", # Lowercase `keyword` imported as non-lowercase `NodeKeyword` ] -"packages/griffe/src/griffe/_internal/debug.py" = [ +"packages/griffelib/src/griffelib/_internal/debug.py" = [ "T201", # Print statement ] -"packages/griffe/src/griffe/_internal/**.py" = [ +"packages/griffelib/src/griffelib/_internal/**.py" = [ "D100", # Missing docstring in public module ] "scripts/*.py" = [ @@ -84,7 +84,7 @@ docstring-quotes = "double" ban-relative-imports = "all" [lint.isort] -known-first-party = ["griffe"] +known-first-party = ["griffe", "griffelib", "griffecli"] [lint.pydocstyle] convention = "google" diff --git a/packages/griffecli/pyproject.toml b/packages/griffecli/pyproject.toml index 26726d5fa..6a9af0fc8 100644 --- a/packages/griffecli/pyproject.toml +++ b/packages/griffecli/pyproject.toml @@ -41,8 +41,17 @@ classifiers = [ "Typing :: Typed", ] +[project.scripts] +griffecli = "griffecli:main" + [tool.hatch.metadata.hooks.uv-dynamic-versioning] +# Dependencies are dynamically versioned; {{version}} is substituted at build time. +dependencies = ["griffelib=={{version}}", "colorama>=0.4"] [tool.hatch.metadata.hooks.uv-dynamic-versioning.optional-dependencies] +# No optional dependencies for the CLI package [tool.hatch.build.targets.sdist] + +[tool.hatch.build.targets.wheel] +sources = ["src/"] diff --git a/packages/griffecli/src/griffecli/__init__.py b/packages/griffecli/src/griffecli/__init__.py index 54cec7a60..b7c23fc12 100644 --- a/packages/griffecli/src/griffecli/__init__.py +++ b/packages/griffecli/src/griffecli/__init__.py @@ -1 +1,27 @@ -# TODO: Cut proper imports from griffelib `__init__.py` module +# This top-level module imports all public names from the CLI package, +# and exposes them as public objects. + +"""Griffe CLI package. + +The CLI (Command Line Interface) for the griffe library. +This package provides command-line tools for interacting with griffe. + +## CLI entrypoints + +- [`griffecli.main`][]: Run the main program. +- [`griffecli.check`][]: Check for API breaking changes in two versions of the same package. +- [`griffecli.dump`][]: Load packages data and dump it as JSON. +- [`griffecli.get_parser`][]: Get the argument parser for the CLI. +""" + +from __future__ import annotations + +from griffecli._internal.cli import DEFAULT_LOG_LEVEL, check, dump, get_parser, main + +__all__ = [ + "DEFAULT_LOG_LEVEL", + "check", + "dump", + "get_parser", + "main", +] diff --git a/packages/griffecli/src/griffecli/__main__.py b/packages/griffecli/src/griffecli/__main__.py index 0374dbb77..22d5b7441 100644 --- a/packages/griffecli/src/griffecli/__main__.py +++ b/packages/griffecli/src/griffecli/__main__.py @@ -1,4 +1,4 @@ -# Entry-point module, in case you use `python -m griffelib`. +# Entry-point module, in case you use `python -m griffecli`. # # Why does this file exist, and why `__main__`? For more info, read: # diff --git a/packages/griffecli/src/griffecli/_internal/__init__.py b/packages/griffecli/src/griffecli/_internal/__init__.py new file mode 100644 index 000000000..03d25ad8f --- /dev/null +++ b/packages/griffecli/src/griffecli/_internal/__init__.py @@ -0,0 +1 @@ +# Internal modules for the griffecli package. diff --git a/packages/griffecli/src/griffecli/_internal/cli.py b/packages/griffecli/src/griffecli/_internal/cli.py index 708b8ac33..b13154dc9 100644 --- a/packages/griffecli/src/griffecli/_internal/cli.py +++ b/packages/griffecli/src/griffecli/_internal/cli.py @@ -4,11 +4,11 @@ # We might be tempted to import things from `__main__` later, # but that will cause problems; the code will get executed twice: # -# - When we run `python -m griffelib`, Python will execute +# - When we run `python -m griffecli`, Python will execute # `__main__.py` as a script. That means there won't be any -# `griffelib.__main__` in `sys.modules`. +# `griffecli.__main__` in `sys.modules`. # - When you import `__main__` it will get executed again (as a module) because -# there's no `griffelib.__main__` in `sys.modules`. +# there's no `griffecli.__main__` in `sys.modules`. from __future__ import annotations diff --git a/packages/griffelib/pyproject.toml b/packages/griffelib/pyproject.toml index aba09f221..b01dfcde7 100644 --- a/packages/griffelib/pyproject.toml +++ b/packages/griffelib/pyproject.toml @@ -42,7 +42,14 @@ classifiers = [ ] [tool.hatch.metadata.hooks.uv-dynamic-versioning] +# No base dependencies needed for griffelib [tool.hatch.metadata.hooks.uv-dynamic-versioning.optional-dependencies] +# The 'pypi' extra provides dependencies needed for the load_pypi functionality +# to download and inspect packages from PyPI. +pypi = ["pip>=24.0", "platformdirs>=4.2", "wheel>=0.42"] [tool.hatch.build.targets.sdist] + +[tool.hatch.build.targets.wheel] +sources = ["src/"] diff --git a/packages/griffelib/src/griffelib/__init__.py b/packages/griffelib/src/griffelib/__init__.py index cba9437d7..53ae93d2b 100644 --- a/packages/griffelib/src/griffelib/__init__.py +++ b/packages/griffelib/src/griffelib/__init__.py @@ -2,17 +2,18 @@ # and exposes them as public objects. We have tests to make sure # no object is forgotten in this list. -"""Griffe package. +"""Griffe library package. Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API. -The entirety of the public API is exposed here, in the top-level `griffelib` module. +The entirety of the library API is exposed here, in the top-level `griffelib` module. +For backward compatibility, you can also import from `griffe` which re-exports everything. All messages written to standard output or error are logged using the `logging` module. -Our logger's name is set to `"griffelib"` and is public (you can rely on it). -You can obtain the logger from the standard `logging` module: `logging.getLogger("griffelib")`. +Our logger's name is set to `"griffe"` and is public (you can rely on it). +You can obtain the logger from the standard `logging` module: `logging.getLogger("griffe")`. Actual logging messages are not part of the public API (they might change without notice). Raised exceptions throughout the package are part of the public API (you can rely on them). @@ -20,68 +21,60 @@ The following paragraphs will help you discover the package's content. -## CLI entrypoints - -Griffe provides a command-line interface (CLI) to interact with the package. The CLI entrypoints can be called from Python code. - -- [`griffecli.main`][]: Run the main program. -- [`griffecli.check`][]: Check for API breaking changes in two versions of the same package. -- [`griffecli.dump`][]: Load packages data and dump it as JSON. - ## Loaders To load API data, Griffe provides several high-level functions. -- [`griffelib.load`][]: Load and return a Griffe object. -- [`griffelib.load_git`][]: Load and return a module from a specific Git reference. -- [`griffelib.load_pypi`][]: Load and return a module from a specific package version downloaded using pip. +- [`griffe.load`][]: Load and return a Griffe object. +- [`griffe.load_git`][]: Load and return a module from a specific Git reference. +- [`griffe.load_pypi`][]: Load and return a module from a specific package version downloaded using pip. ## Models The data loaded by Griffe is represented by several classes. -- [`griffelib.Module`][]: The class representing a Python module. -- [`griffelib.Class`][]: The class representing a Python class. -- [`griffelib.Function`][]: The class representing a Python function or method. -- [`griffelib.Attribute`][]: The class representing a Python attribute. -- [`griffelib.Alias`][]: This class represents an alias, or indirection, to an object declared in another module. +- [`griffe.Module`][]: The class representing a Python module. +- [`griffe.Class`][]: The class representing a Python class. +- [`griffe.Function`][]: The class representing a Python function or method. +- [`griffe.Attribute`][]: The class representing a Python attribute. +- [`griffe.Alias`][]: This class represents an alias, or indirection, to an object declared in another module. Additional classes are available to represent other concepts. -- [`griffelib.Decorator`][]: This class represents a decorator. -- [`griffelib.Parameters`][]: This class is a container for parameters. -- [`griffelib.Parameter`][]: This class represent a function parameter. +- [`griffe.Decorator`][]: This class represents a decorator. +- [`griffe.Parameters`][]: This class is a container for parameters. +- [`griffe.Parameter`][]: This class represent a function parameter. ## Agents Griffe is able to analyze code both statically and dynamically, using the following "agents". However most of the time you will only need to use the loaders above. -- [`griffelib.visit`][]: Parse and visit a module file. -- [`griffelib.inspect`][]: Inspect a module. +- [`griffe.visit`][]: Parse and visit a module file. +- [`griffe.inspect`][]: Inspect a module. ## Serializers Griffe can serizalize data to dictionary and JSON. -- [`griffelib.Object.as_json`][griffelib.Object.as_json] -- [`griffelib.Object.from_json`][griffelib.Object.from_json] -- [`griffelib.JSONEncoder`][]: JSON encoder for Griffe objects. -- [`griffelib.json_decoder`][]: JSON decoder for Griffe objects. +- [`griffe.Object.as_json`][griffe.Object.as_json] +- [`griffe.Object.from_json`][griffe.Object.from_json] +- [`griffe.JSONEncoder`][]: JSON encoder for Griffe objects. +- [`griffe.json_decoder`][]: JSON decoder for Griffe objects. ## API checks Griffe can compare two versions of the same package to find breaking changes. -- [`griffelib.find_breaking_changes`][]: Find breaking changes between two versions of the same API. -- [`griffelib.Breakage`][]: Breakage classes can explain what broke from a version to another. +- [`griffe.find_breaking_changes`][]: Find breaking changes between two versions of the same API. +- [`griffe.Breakage`][]: Breakage classes can explain what broke from a version to another. ## Extensions -Griffe supports extensions. You can create your own extension by subclassing the `griffelib.Extension` class. +Griffe supports extensions. You can create your own extension by subclassing the `griffe.Extension` class. -- [`griffelib.load_extensions`][]: Load configured extensions. -- [`griffelib.Extension`][]: Base class for Griffe extensions. +- [`griffe.load_extensions`][]: Load configured extensions. +- [`griffe.Extension`][]: Base class for Griffe extensions. ## Docstrings @@ -89,35 +82,35 @@ Main class: -- [`griffelib.Docstring`][]: This class represents docstrings. +- [`griffe.Docstring`][]: This class represents docstrings. Docstring section and element classes all start with `Docstring`. Docstring parsers: -- [`griffelib.parse`][]: Parse the docstring. -- [`griffelib.parse_auto`][]: Parse a docstring by automatically detecting the style it uses. -- [`griffelib.parse_google`][]: Parse a Google-style docstring. -- [`griffelib.parse_numpy`][]: Parse a Numpydoc-style docstring. -- [`griffelib.parse_sphinx`][]: Parse a Sphinx-style docstring. +- [`griffe.parse`][]: Parse the docstring. +- [`griffe.parse_auto`][]: Parse a docstring by automatically detecting the style it uses. +- [`griffe.parse_google`][]: Parse a Google-style docstring. +- [`griffe.parse_numpy`][]: Parse a Numpydoc-style docstring. +- [`griffe.parse_sphinx`][]: Parse a Sphinx-style docstring. ## Exceptions Griffe uses several exceptions to signal errors. -- [`griffelib.GriffeError`][]: The base exception for all Griffe errors. -- [`griffelib.LoadingError`][]: Exception for loading errors. -- [`griffelib.NameResolutionError`][]: Exception for names that cannot be resolved in a object scope. -- [`griffelib.UnhandledEditableModuleError`][]: Exception for unhandled editables modules, when searching modules. -- [`griffelib.UnimportableModuleError`][]: Exception for modules that cannot be imported. -- [`griffelib.AliasResolutionError`][]: Exception for aliases that cannot be resolved. -- [`griffelib.CyclicAliasError`][]: Exception raised when a cycle is detected in aliases. -- [`griffelib.LastNodeError`][]: Exception raised when trying to access a next or previous node. -- [`griffelib.RootNodeError`][]: Exception raised when trying to use siblings properties on a root node. -- [`griffelib.BuiltinModuleError`][]: Exception raised when trying to access the filepath of a builtin module. -- [`griffelib.ExtensionError`][]: Base class for errors raised by extensions. -- [`griffelib.ExtensionNotLoadedError`][]: Exception raised when an extension could not be loaded. -- [`griffelib.GitError`][]: Exception raised for errors related to Git. +- [`griffe.GriffeError`][]: The base exception for all Griffe errors. +- [`griffe.LoadingError`][]: Exception for loading errors. +- [`griffe.NameResolutionError`][]: Exception for names that cannot be resolved in a object scope. +- [`griffe.UnhandledEditableModuleError`][]: Exception for unhandled editables modules, when searching modules. +- [`griffe.UnimportableModuleError`][]: Exception for modules that cannot be imported. +- [`griffe.AliasResolutionError`][]: Exception for aliases that cannot be resolved. +- [`griffe.CyclicAliasError`][]: Exception raised when a cycle is detected in aliases. +- [`griffe.LastNodeError`][]: Exception raised when trying to access a next or previous node. +- [`griffe.RootNodeError`][]: Exception raised when trying to use siblings properties on a root node. +- [`griffe.BuiltinModuleError`][]: Exception raised when trying to access the filepath of a builtin module. +- [`griffe.ExtensionError`][]: Base class for errors raised by extensions. +- [`griffe.ExtensionNotLoadedError`][]: Exception raised when an extension could not be loaded. +- [`griffe.GitError`][]: Exception raised for errors related to Git. # Expressions @@ -125,20 +118,20 @@ Expressions are basically abstract syntax trees (AST) with a few differences compared to the nodes returned by [`ast`][]. Griffe provides a few helpers to extract expressions from regular AST nodes. -- [`griffelib.get_annotation`][]: Get a type annotation as expression. -- [`griffelib.get_base_class`][]: Get a base class as expression. -- [`griffelib.get_class_keyword`][]: Get a class keyword as expression. -- [`griffelib.get_condition`][]: Get a condition as expression. -- [`griffelib.get_expression`][]: Get an expression from an AST node. -- [`griffelib.safe_get_annotation`][]: Get a type annotation as expression, safely (returns `None` on error). -- [`griffelib.safe_get_base_class`][]: Get a base class as expression, safely (returns `None` on error). -- [`griffelib.safe_get_class_keyword`][]: Get a class keyword as expression, safely (returns `None` on error). -- [`griffelib.safe_get_condition`][]: Get a condition as expression, safely (returns `None` on error). -- [`griffelib.safe_get_expression`][]: Get an expression from an AST node, safely (returns `None` on error). +- [`griffe.get_annotation`][]: Get a type annotation as expression. +- [`griffe.get_base_class`][]: Get a base class as expression. +- [`griffe.get_class_keyword`][]: Get a class keyword as expression. +- [`griffe.get_condition`][]: Get a condition as expression. +- [`griffe.get_expression`][]: Get an expression from an AST node. +- [`griffe.safe_get_annotation`][]: Get a type annotation as expression, safely (returns `None` on error). +- [`griffe.safe_get_base_class`][]: Get a base class as expression, safely (returns `None` on error). +- [`griffe.safe_get_class_keyword`][]: Get a class keyword as expression, safely (returns `None` on error). +- [`griffe.safe_get_condition`][]: Get a condition as expression, safely (returns `None` on error). +- [`griffe.safe_get_expression`][]: Get an expression from an AST node, safely (returns `None` on error). The base class for expressions. -- [`griffelib.Expr`][] +- [`griffe.Expr`][] Expression classes all start with `Expr`. @@ -147,26 +140,23 @@ If you want to log messages from extensions, get a logger with `get_logger`. The `logger` attribute is used by Griffe itself. You can use it to temporarily disable Griffe logging. -- [`griffelib.logger`][]: Our global logger, used throughout the library. -- [`griffelib.get_logger`][]: Create and return a new logger instance. +- [`griffe.logger`][]: Our global logger, used throughout the library. +- [`griffe.get_logger`][]: Create and return a new logger instance. # Helpers To test your Griffe extensions, or to load API data from code in memory, Griffe provides the following helpers. -- [`griffelib.temporary_pyfile`][]: Create a Python file containing the given code in a temporary directory. -- [`griffelib.temporary_pypackage`][]: Create a package containing the given modules in a temporary directory. -- [`griffelib.temporary_visited_module`][]: Create and visit a temporary module with the given code. -- [`griffelib.temporary_visited_package`][]: Create and visit a temporary package. -- [`griffelib.temporary_inspected_module`][]: Create and inspect a temporary module with the given code. -- [`griffelib.temporary_inspected_package`][]: Create and inspect a temporary package. +- [`griffe.temporary_pyfile`][]: Create a Python file containing the given code in a temporary directory. +- [`griffe.temporary_pypackage`][]: Create a package containing the given modules in a temporary directory. +- [`griffe.temporary_visited_module`][]: Create and visit a temporary module with the given code. +- [`griffe.temporary_visited_package`][]: Create and visit a temporary package. +- [`griffe.temporary_inspected_module`][]: Create and inspect a temporary module with the given code. +- [`griffe.temporary_inspected_package`][]: Create and inspect a temporary package. """ from __future__ import annotations -import warnings -from typing import Any - from griffelib._internal.agents.inspector import Inspector, inspect from griffelib._internal.agents.nodes.assignments import get_instance_names, get_name, get_names from griffelib._internal.agents.nodes.ast import ( @@ -188,7 +178,6 @@ from griffelib._internal.agents.nodes.values import get_value, safe_get_value from griffelib._internal.agents.visitor import Visitor, builtin_decorators, stdlib_decorators, typing_overload, visit from griffelib._internal.c3linear import c3linear_merge -from griffecli._internal.cli import DEFAULT_LOG_LEVEL, check, dump, get_parser, main from griffelib._internal.collections import LinesCollection, ModulesCollection from griffelib._internal.diff import ( AttributeChangedTypeBreakage, @@ -386,7 +375,6 @@ # names = sorted(n for n in dir(griffelib) if not n.startswith("_") and n not in ("Any", "annotations", "lazy_importing", "warnings")) # print('__all__ = [\n "' + '",\n "'.join(names) + '",\n]') __all__ = [ - "DEFAULT_LOG_LEVEL", "Alias", "AliasResolutionError", "Attribute", @@ -537,7 +525,6 @@ "UnimportableModuleError", "UnpackTypedDictExtension", "Visitor", - "assert_git_repo", "ast_children", "ast_first_child", "ast_kind", @@ -550,9 +537,7 @@ "builtin_decorators", "builtin_extensions", "c3linear_merge", - "check", "docstring_warning", - "dump", "dynamic_import", "find_breaking_changes", "get__all__", @@ -563,13 +548,10 @@ "get_docstring", "get_expression", "get_instance_names", - "get_latest_tag", "get_logger", "get_name", "get_names", "get_parameters", - "get_parser", - "get_repo_root", "get_value", "htree", "infer_docstring_style", @@ -580,7 +562,6 @@ "load_git", "load_pypi", "logger", - "main", "merge_stubs", "module_vtree", "parse", @@ -607,7 +588,6 @@ "temporary_pypackage", "temporary_visited_module", "temporary_visited_package", - "tmp_worktree", "typing_overload", "visit", "vtree", diff --git a/packages/griffelib/src/griffelib/_internal/extensions/base.py b/packages/griffelib/src/griffelib/_internal/extensions/base.py index 879bf23af..c51893ee8 100644 --- a/packages/griffelib/src/griffelib/_internal/extensions/base.py +++ b/packages/griffelib/src/griffelib/_internal/extensions/base.py @@ -603,7 +603,7 @@ def load_extensions(*exts: LoadableExtensionType) -> Extensions: # TODO: Deprecate and remove at some point? # Always add our built-in dataclasses extension. - from griffelib._internal.extensions.dataclasses import DataclassesExtension # noqa: PLC0415 + from griffelib._internal.extensions.dataclasses import DataclassesExtension # noqa: PLC0415 for ext in extensions._extensions: if type(ext) is DataclassesExtension: diff --git a/packages/griffelib/src/griffelib/_internal/logger.py b/packages/griffelib/src/griffelib/_internal/logger.py index 3e598a758..4a7d45773 100644 --- a/packages/griffelib/src/griffelib/_internal/logger.py +++ b/packages/griffelib/src/griffelib/_internal/logger.py @@ -4,7 +4,8 @@ # For example, mkdocstrings-python patches the logger to relocate it as a child # of `mkdocs.plugins` so that it fits in the MkDocs logging configuration. # -# We use a single, global logger because our public API is exposed in a single module, `griffelib`. +# We use a single, global logger because our public API is exposed in a single module. +# The logger name is "griffe" for backward compatibility. # Extensions however should use their own logger, which is why we provide the `get_logger` function. from __future__ import annotations @@ -40,7 +41,7 @@ def disable(self) -> Iterator[None]: self._logger.setLevel(old_level) @classmethod - def _get(cls, name: str = "griffelib") -> Logger: + def _get(cls, name: str = "griffe") -> Logger: if name not in cls._instances: cls._instances[name] = cls(name) return cls._instances[name] @@ -60,7 +61,7 @@ def _patch_loggers(cls, get_logger_func: Callable) -> None: Griffe's output and error messages are logging messages. -Griffe provides the [`patch_loggers`][griffelib.patch_loggers] +Griffe provides the [`patch_loggers`][griffe.patch_loggers] function so dependent libraries can patch Griffe loggers as they see fit. For example, to fit in the MkDocs logging configuration @@ -68,7 +69,7 @@ def _patch_loggers(cls, get_logger_func: Callable) -> None: ```python import logging -from griffelib import patch_loggers +from griffe import patch_loggers class LoggerAdapter(logging.LoggerAdapter): @@ -90,7 +91,7 @@ def get_logger(name): """ -def get_logger(name: str = "griffelib") -> Logger: +def get_logger(name: str = "griffe") -> Logger: """Create and return a new logger instance. Parameters: diff --git a/packages/griffelib/src/griffelib/_internal/models.py b/packages/griffelib/src/griffelib/_internal/models.py index fde780f6e..50a38814f 100644 --- a/packages/griffelib/src/griffelib/_internal/models.py +++ b/packages/griffelib/src/griffelib/_internal/models.py @@ -14,7 +14,12 @@ from griffelib._internal.c3linear import c3linear_merge from griffelib._internal.docstrings.parsers import DocstringOptions, DocstringStyle, parse from griffelib._internal.enumerations import Kind, ParameterKind, Parser, TypeParameterKind -from griffelib._internal.exceptions import AliasResolutionError, BuiltinModuleError, CyclicAliasError, NameResolutionError +from griffelib._internal.exceptions import ( + AliasResolutionError, + BuiltinModuleError, + CyclicAliasError, + NameResolutionError, +) from griffelib._internal.expressions import ExprCall, ExprName, ExprTuple from griffelib._internal.logger import logger from griffelib._internal.mixins import ObjectAliasMixin diff --git a/pyproject.toml b/pyproject.toml index 3a2fff8cc..3bddfc0db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [build-system] # pdm-backend is left here as a dependency of the version discovery script currently in use. # It may be removed in the future. See mkdocstrings/griffe#430 -requires = ["hatchling", "pdm-backend"] +requires = ["hatchling", "pdm-backend", "uv-dynamic-versioning>=0.7.0"] build-backend = "hatchling.build" [project] @@ -13,7 +13,7 @@ license-files = ["LICENSE"] readme = "README.md" requires-python = ">=3.10" keywords = ["api", "signature", "breaking-changes", "static-analysis", "dynamic-analysis"] -dynamic = ["version"] +dynamic = ["version", "dependencies", "optional-dependencies"] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", @@ -36,16 +36,6 @@ classifiers = [ "Topic :: Utilities", "Typing :: Typed", ] -dependencies = [ - "colorama>=0.4", -] - -[project.optional-dependencies] -pypi = [ - "pip>=24.0", - "platformdirs>=4.2", - "wheel>=0.42", -] [project.urls] Homepage = "https://mkdocstrings.github.io/griffe" @@ -65,6 +55,15 @@ source = "code" path = "scripts/get_version.py" expression = "get_version()" +[tool.hatch.metadata.hooks.uv-dynamic-versioning] +# Dependencies are dynamically versioned; {{version}} is substituted at build time. +# This ensures griffe, griffelib, and griffecli versions are always 1:1. +dependencies = ["griffelib=={{version}}", "griffecli=={{version}}"] + +[tool.hatch.metadata.hooks.uv-dynamic-versioning.optional-dependencies] +# The 'pypi' extra re-exports griffelib[pypi] for backward compatibility. +pypi = ["griffelib[pypi]=={{version}}"] + [tool.hatch.build] # Include as much as possible in the source distribution, to help redistributors. ignore-vcs = true @@ -136,3 +135,4 @@ ci = [ [tool.uv] default-groups = ["maintain", "ci", "docs"] +workspace = { members = ["packages/*"] } diff --git a/src/griffe/__init__.py b/src/griffe/__init__.py index a48bb5aa2..09ddc836a 100644 --- a/src/griffe/__init__.py +++ b/src/griffe/__init__.py @@ -1,8 +1,28 @@ -"""Griffe CLI package. Re-exports griffelib and griffecli.""" +"""Griffe package. -from griffecli import * +Signatures for entire Python programs. +Extract the structure, the frame, the skeleton of your project, +to generate API documentation or find breaking changes in your API. + +This is a backward-compatible package that re-exports all public symbols from +both `griffelib` (the library) and `griffecli` (the CLI). + +The entirety of the public API is exposed here, in the top-level `griffe` module. + +All messages written to standard output or error are logged using the `logging` module. +Our logger's name is set to `"griffe"` and is public (you can rely on it). +You can obtain the logger from the standard `logging` module: `logging.getLogger("griffe")`. +Actual logging messages are not part of the public API (they might change without notice). + +Raised exceptions throughout the package are part of the public API (you can rely on them). +Their actual messages are not part of the public API (they might change without notice). + +See the `griffelib` and `griffecli` packages for detailed API documentation. +""" + +from griffecli import * # noqa: F403 from griffecli import __all__ as __cli_all__ -from griffelib import * +from griffelib import * # noqa: F403 from griffelib import __all__ as __lib_all__ -__all__ = [*__lib_all__, *__cli_all__] +__all__ = [*__lib_all__, *__cli_all__] # noqa: PLE0604 diff --git a/src/griffe/__main__.py b/src/griffe/__main__.py index 3b5ca458b..30c64ab2b 100644 --- a/src/griffe/__main__.py +++ b/src/griffe/__main__.py @@ -1,3 +1,11 @@ +"""Entry-point module, in case you use `python -m griffe`. + +Why does this file exist, and why `__main__`? For more info, read: + +- https://www.python.org/dev/peps/pep-0338/ +- https://docs.python.org/3/using/cmdline.html#cmdoption-m +""" + import sys from griffecli import main diff --git a/tests/test_cli.py b/tests/test_cli.py index c500a3ac2..fde01915f 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -6,7 +6,8 @@ import pytest -from griffelib._internal import cli, debug +from griffecli._internal import cli +from griffelib._internal import debug def test_main() -> None: diff --git a/tests/test_encoders.py b/tests/test_encoders.py index b8d3eeb47..938688194 100644 --- a/tests/test_encoders.py +++ b/tests/test_encoders.py @@ -73,7 +73,7 @@ def test_namespace_packages() -> None: def test_minimal_light_data_is_enough(symbol: str) -> None: """Test serialization and de-serialization.""" loader = GriffeLoader() - package = loader.load("griffe") + package = loader.load("griffelib") obj = package[symbol] dump_options = {"indent": 2, "sort_keys": True} minimal = obj.as_json(full=False, **dump_options) diff --git a/tests/test_finder.py b/tests/test_finder.py index c24c4622d..d65c492fc 100644 --- a/tests/test_finder.py +++ b/tests/test_finder.py @@ -125,7 +125,7 @@ def test_editables_file_handling(tmp_path: Path, editable_file_name: str) -> Non tmp_path: Pytest fixture. """ pth_file = tmp_path / editable_file_name - pth_file.write_text("hello\nF.map_module('griffe', 'packages/griffe/src/griffe/__init__.py')", encoding="utf8") + pth_file.write_text("hello\nF.map_module('griffe', 'src/griffe/__init__.py')", encoding="utf8") paths = [sp.path for sp in _handle_editable_module(pth_file)] assert paths == [Path("src")] @@ -139,7 +139,7 @@ def test_setuptools_file_handling(tmp_path: Path, annotation: str) -> None: annotation: The type annotation of the MAPPING variable. """ pth_file = tmp_path / "__editable__whatever.py" - pth_file.write_text(f"hello\nMAPPING{annotation} = {{'griffe': 'packages/griffe/src/griffe'}}", encoding="utf8") + pth_file.write_text(f"hello\nMAPPING{annotation} = {{'griffe': 'src/griffe'}}", encoding="utf8") paths = [sp.path for sp in _handle_editable_module(pth_file)] assert paths == [Path("src")]