Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
2,840 additions
and
23 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
"""Autogenerate Poetry package manifests in a monorepo""" | ||
|
||
__version__ = "0.1.0" | ||
__version__ = "0.2.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
"""Contains the CLI""" | ||
|
||
import logging | ||
from pathlib import Path | ||
from typing import Optional | ||
|
||
import click | ||
|
||
from ns_poet.processor import PackageProcessor | ||
from ns_poet.project import PROJECT_CONFIG | ||
from ns_poet.requirements import update_import_map | ||
|
||
logging.basicConfig(level=logging.INFO) | ||
logger = logging.getLogger(__name__) | ||
|
||
|
||
@click.group() | ||
def cli() -> None: | ||
"""Autogenerate Poetry package manifests in a monorepo""" | ||
pass | ||
|
||
|
||
@cli.group(name="import-map") | ||
def import_map() -> None: | ||
"""Commands for managing imports""" | ||
pass | ||
|
||
|
||
@import_map.command() | ||
def update() -> None: | ||
"""Update an import map from requirements.txt""" | ||
update_import_map() | ||
|
||
|
||
@cli.group() | ||
def package() -> None: | ||
"""Commands for managing packages""" | ||
pass | ||
|
||
|
||
@package.command() | ||
@click.option( | ||
"-p", | ||
"--package-path", | ||
type=click.Path(exists=True, dir_okay=True, file_okay=False, path_type=Path), | ||
help="Generate a package manifest for a single package path", | ||
) | ||
def generate(package_path: Optional[Path]) -> None: | ||
"""Generate Poetry package manifests""" | ||
PROJECT_CONFIG.load_requirements() | ||
processor = PackageProcessor() | ||
processor.register_packages() | ||
processor.ensure_no_circular_imports() | ||
if package_path: | ||
processor.generate_package_manifest(package_path) | ||
else: | ||
processor.generate_package_manifests() |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
"""Contains exception classes""" | ||
|
||
|
||
class CircularImportsFound(Exception): | ||
"""Raised when circular imports were found processing build targets""" | ||
|
||
pass | ||
|
||
|
||
class NoBuildDependenciesFound(Exception): | ||
"""Raised when the dependencies argument could not be found in a BUILD file AST""" | ||
|
||
pass | ||
|
||
|
||
class DistributionError(Exception): | ||
"""Raised when a Python distribution can't be parsed""" | ||
|
||
pass | ||
|
||
|
||
class UnsupportedDistributionFormat(DistributionError): | ||
"""Raised when the distribution file is something other than a .whl or .tar.gz""" | ||
|
||
pass | ||
|
||
|
||
class MissingDistributionMetadataFile(DistributionError): | ||
"""Raised when there is a missing metadata file in a .whl or .tar.gz""" | ||
|
||
pass | ||
|
||
|
||
class NoProjectName(Exception): | ||
"""Raised when no project name could be parsed from distribution metadata""" | ||
|
||
pass | ||
|
||
|
||
class InvalidTopLevelFile(Exception): | ||
"""Raised when a top_level.txt file could not be interpreted""" | ||
|
||
pass | ||
|
||
|
||
class MultipleSourcePackagesFound(Exception): | ||
"""Raised when more than one package is found in src/""" | ||
|
||
pass | ||
|
||
|
||
class DuplicateTarget(Exception): | ||
"""Raised when attempting to register a target that has an existing key""" | ||
|
||
pass | ||
|
||
|
||
class NoConsoleScriptFound(Exception): | ||
"""Raised when no console_scripts are found in setup.py for a binary target""" | ||
|
||
pass | ||
|
||
|
||
class NoTargetFound(Exception): | ||
"""Raised when no target could be found in the registered graph of targets""" | ||
|
||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
"""Contains PoetPackage class""" | ||
|
||
from pathlib import Path | ||
from typing import Any, Dict, MutableMapping, Optional | ||
|
||
import toml | ||
|
||
|
||
class PoetPackage: | ||
"""Class that represents a package managed by ns-poet | ||
This object manages a Python package's pyproject.toml (i.e. manifest) file. | ||
The schema for the configuration file in the git project root is:: | ||
[tool.nspoet] | ||
generate_package_manifest = true | ||
""" | ||
|
||
def __init__(self, package_path: Path) -> None: | ||
"""Initializer | ||
Args: | ||
package_path: Path to the package | ||
""" | ||
self.package_path = package_path | ||
self.config_file_path = package_path.joinpath("pyproject.toml") | ||
self._config: Optional[MutableMapping[str, Any]] = None | ||
|
||
@classmethod | ||
def from_path(cls, package_path: Path) -> "PoetPackage": | ||
"""Create a package object and load configuration from a given package path | ||
Args: | ||
package_path: Path to the package | ||
Returns: | ||
new PoetPackage instance | ||
""" | ||
p = cls(package_path) | ||
p.load_config() | ||
return p | ||
|
||
def load_config(self) -> MutableMapping[str, Any]: | ||
"""Load configuration from disk | ||
Returns: | ||
configuration dict | ||
""" | ||
if self.config_file_path.is_file(): | ||
with self.config_file_path.open() as f: | ||
self._config = toml.load(f) | ||
else: | ||
self._config = {} | ||
|
||
return self._config | ||
|
||
def save_config(self) -> None: | ||
"""Save the configuration object to disk""" | ||
if self.generate_package_manifest: | ||
with self.config_file_path.open("w") as f: | ||
toml.dump( # type: ignore | ||
self._config, | ||
f, | ||
encoder=toml.encoder.TomlPreserveInlineDictEncoder(), # type: ignore | ||
) | ||
|
||
def to_string(self) -> str: | ||
"""Dump the configuration to a TOML string""" | ||
return toml.dumps( # type: ignore | ||
self._config, encoder=toml.encoder.TomlPreserveInlineDictEncoder() # type: ignore | ||
) | ||
|
||
@property | ||
def package_config(self) -> Dict[str, Any]: | ||
"""Return the nspoet configuration subsection within the manifest""" | ||
return self._config.get("tool", {}).get("nspoet", {}) | ||
|
||
@property | ||
def generate_package_manifest(self) -> bool: | ||
"""Flag denoting whether to generate a package manifest file""" | ||
return self.package_config.get("generate_package_manifest", True) | ||
|
||
def update( | ||
self, name: str, dependencies: Dict[str, str], dev_dependencies: Dict[str, str] | ||
) -> None: | ||
"""Update the package configuration in place | ||
Args: | ||
name: package name | ||
dependencies: map of dependency name to version specifier | ||
dev_dependencies: map of development dependency name to version specifier | ||
""" | ||
self._config.setdefault("tool", {}) | ||
self._config["tool"].setdefault("poetry", {}) | ||
self._config["tool"]["poetry"]["name"] = name | ||
self._config["tool"]["poetry"]["version"] = "1.0.0" | ||
self._config["tool"]["poetry"]["description"] = "" | ||
self._config["tool"]["poetry"]["authors"] = [] | ||
self._config["tool"]["poetry"]["license"] = "Proprietary" | ||
self._config["tool"]["poetry"]["dependencies"] = dependencies | ||
self._config["tool"]["poetry"]["dev-dependencies"] = dev_dependencies | ||
self._config["build-system"] = { | ||
"requires": ["poetry-core>=1.0.0"], | ||
"build-backend": "poetry.core.masonry.api", | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
"""Defines subpackage exports""" | ||
from .base import BuildTarget | ||
from .python_package import PythonPackage | ||
from .requirement import PythonRequirement |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
"""Contains base BuildTarget class""" | ||
from abc import ABC | ||
from functools import total_ordering | ||
from typing import Any | ||
|
||
|
||
@total_ordering | ||
class BuildTarget(ABC): | ||
"""Represents a Python build target in Pants. | ||
This should be used as a mixin to provide common attributes and methods. | ||
""" | ||
|
||
# This string will be used as an identifier for the build target | ||
key: str = None | ||
|
||
def __str__(self) -> str: | ||
"""String representation""" | ||
return str(self.key) | ||
|
||
def __eq__(self, other: Any) -> bool: | ||
"""Equality check""" | ||
if hasattr(other, "key"): | ||
return str(self.key) == str(other.key) | ||
else: | ||
return False | ||
|
||
def __hash__(self) -> int: | ||
"""Object hash""" | ||
return hash((str(self.key),)) | ||
|
||
def __repr__(self) -> str: | ||
"""Object representation for debugging""" | ||
return str(self.key) | ||
|
||
def __lt__(self, other: Any) -> bool: | ||
"""Less than comparator""" | ||
if hasattr(other, "key"): | ||
return str(self.key) < str(other.key) | ||
else: | ||
return False | ||
|
||
@property | ||
def dependency_target(self) -> str: | ||
"""Returns the representation of this target in another target's dependencies""" | ||
return str(self.key) |
Oops, something went wrong.