Skip to content

Commit

Permalink
[internal] Update python/ to use new Options API (pantsbuild#14369)
Browse files Browse the repository at this point in the history
[ci skip-rust]
  • Loading branch information
thejcannon authored Feb 5, 2022
1 parent 3bea03c commit 7e31f4b
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 274 deletions.
159 changes: 60 additions & 99 deletions src/python/pants/backend/python/dependency_inference/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
)
from pants.engine.unions import UnionRule
from pants.option.global_options import OwnersNotFoundBehavior
from pants.option.option_types import BoolOption, EnumOption, IntOption
from pants.option.subsystem import Subsystem
from pants.util.docutil import doc_url
from pants.util.strutil import bullet_list
Expand All @@ -64,105 +65,65 @@ class PythonInferSubsystem(Subsystem):
options_scope = "python-infer"
help = "Options controlling which dependencies will be inferred for Python targets."

@classmethod
def register_options(cls, register):
super().register_options(register)
register(
"--imports",
default=True,
type=bool,
help=(
"Infer a target's imported dependencies by parsing import statements from sources."
),
)
register(
"--string-imports",
default=False,
type=bool,
help=(
"Infer a target's dependencies based on strings that look like dynamic "
"dependencies, such as Django settings files expressing dependencies as strings. "
"To ignore any false positives, put `!{bad_address}` in the `dependencies` field "
"of your target."
),
)
register(
"--string-imports-min-dots",
default=2,
type=int,
help=(
"If --string-imports is True, treat valid-looking strings with at least this many "
"dots in them as potential dynamic dependencies. E.g., `'foo.bar.Baz'` will be "
"treated as a potential dependency if this option is set to 2 but not if set to 3."
),
)
register(
"--inits",
default=False,
type=bool,
help=(
"Infer a target's dependencies on any `__init__.py` files in the packages "
"it is located in (recursively upward in the directory structure).\n\nEven if this "
"is disabled, Pants will still include any ancestor `__init__.py` files, only they "
"will not be 'proper' dependencies, e.g. they will not show up in "
"`./pants dependencies` and their own dependencies will not be used.\n\nIf you "
"have empty `__init__.py` files, it's safe to leave this option off; otherwise, "
"you should enable this option."
),
)
register(
"--conftests",
default=True,
type=bool,
help=(
"Infer a test target's dependencies on any conftest.py files in the current "
"directory and ancestor directories."
),
)
register(
"--entry-points",
default=True,
type=bool,
help=(
"Infer dependencies on targets' entry points, e.g. `pex_binary`'s "
"`entry_point` field, `python_awslambda`'s `handler` field and "
"`python_distribution`'s `entry_points` field."
),
)
register(
"--unowned-dependency-behavior",
type=UnownedDependencyUsage,
default=UnownedDependencyUsage.DoNothing,
help=("How to handle inferred dependencies that don't have any owner."),
)

@property
def imports(self) -> bool:
return cast(bool, self.options.imports)

@property
def string_imports(self) -> bool:
return cast(bool, self.options.string_imports)

@property
def string_imports_min_dots(self) -> int:
return cast(int, self.options.string_imports_min_dots)

@property
def inits(self) -> bool:
return cast(bool, self.options.inits)

@property
def conftests(self) -> bool:
return cast(bool, self.options.conftests)

@property
def entry_points(self) -> bool:
return cast(bool, self.options.entry_points)

@property
def unowned_dependency_behavior(self) -> UnownedDependencyUsage:
return cast(UnownedDependencyUsage, self.options.unowned_dependency_behavior)
imports = BoolOption(
"--imports",
default=True,
help=("Infer a target's imported dependencies by parsing import statements from sources."),
)
string_imports = BoolOption(
"--string-imports",
default=False,
help=(
"Infer a target's dependencies based on strings that look like dynamic "
"dependencies, such as Django settings files expressing dependencies as strings. "
"To ignore any false positives, put `!{bad_address}` in the `dependencies` field "
"of your target."
),
)
string_imports_min_dots = IntOption(
"--string-imports-min-dots",
default=2,
help=(
"If --string-imports is True, treat valid-looking strings with at least this many "
"dots in them as potential dynamic dependencies. E.g., `'foo.bar.Baz'` will be "
"treated as a potential dependency if this option is set to 2 but not if set to 3."
),
)
inits = BoolOption(
"--inits",
default=False,
help=(
"Infer a target's dependencies on any `__init__.py` files in the packages "
"it is located in (recursively upward in the directory structure).\n\nEven if this "
"is disabled, Pants will still include any ancestor `__init__.py` files, only they "
"will not be 'proper' dependencies, e.g. they will not show up in "
"`./pants dependencies` and their own dependencies will not be used.\n\nIf you "
"have empty `__init__.py` files, it's safe to leave this option off; otherwise, "
"you should enable this option."
),
)
conftests = BoolOption(
"--conftests",
default=True,
help=(
"Infer a test target's dependencies on any conftest.py files in the current "
"directory and ancestor directories."
),
)
entry_points = BoolOption(
"--entry-points",
default=True,
help=(
"Infer dependencies on targets' entry points, e.g. `pex_binary`'s "
"`entry_point` field, `python_awslambda`'s `handler` field and "
"`python_distribution`'s `entry_points` field."
),
)
unowned_dependency_behavior = EnumOption(
"--unowned-dependency-behavior",
default=UnownedDependencyUsage.DoNothing,
help=("How to handle inferred dependencies that don't have any owner."),
)


class InferPythonImportDependencies(InferDependenciesRequest):
Expand Down
2 changes: 1 addition & 1 deletion src/python/pants/backend/python/goals/lockfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def warn_python_repos(option: str) -> None:

if python_repos.repos:
warn_python_repos("repos")
if python_repos.indexes != [python_repos.pypi_index]:
if python_repos.indexes != (python_repos.pypi_index,):
warn_python_repos("indexes")
return MaybeWarnPythonRepos()

Expand Down
58 changes: 25 additions & 33 deletions src/python/pants/backend/python/goals/setup_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
targets_with_sources_types,
)
from pants.engine.unions import UnionMembership, UnionRule, union
from pants.option.option_types import BoolOption, EnumOption
from pants.option.subsystem import Subsystem
from pants.util.docutil import doc_url
from pants.util.frozendict import FrozenDict
Expand Down Expand Up @@ -304,46 +305,37 @@ class SetupPyGeneration(Subsystem):
options_scope = "setup-py-generation"
help = "Options to control how setup.py is generated from a `python_distribution` target."

@classmethod
def register_options(cls, register):
super().register_options(register)
# Generating setup is the more aggressive thing to do, so we'd prefer that the default
# be False. However that would break widespread existing usage, so we'll make that
# change in a future deprecation cycle.
register(
"--generate-setup-default",
type=bool,
default=True,
help=(
"The default value for the `generate_setup` field on `python_distribution` targets."
"Can be overridden per-target by setting that field explicitly. Set this to False "
"if you mostly rely on handwritten setup files (setup.py, setup.cfg and similar). "
"Leave as True if you mostly rely on Pants generating setup files for you."
),
)

register(
"--first-party-dependency-version-scheme",
type=FirstPartyDependencyVersionScheme,
default=FirstPartyDependencyVersionScheme.EXACT,
help=(
"What version to set in `install_requires` when a `python_distribution` depends on "
"other `python_distribution`s. If `exact`, will use `==`. If `compatible`, will "
"use `~=`. If `any`, will leave off the version. See "
"https://www.python.org/dev/peps/pep-0440/#version-specifiers."
),
)
# Generating setup is the more aggressive thing to do, so we'd prefer that the default
# be False. However that would break widespread existing usage, so we'll make that
# change in a future deprecation cycle.
generate_setup_default = BoolOption(
"--generate-setup-default",
default=True,
help=(
"The default value for the `generate_setup` field on `python_distribution` targets."
"Can be overridden per-target by setting that field explicitly. Set this to False "
"if you mostly rely on handwritten setup files (setup.py, setup.cfg and similar). "
"Leave as True if you mostly rely on Pants generating setup files for you."
),
)

@property
def generate_setup_default(self) -> bool:
return cast(bool, self.options.generate_setup_default)
first_party_dependency_version_scheme = EnumOption(
"--first-party-dependency-version-scheme",
default=FirstPartyDependencyVersionScheme.EXACT,
help=(
"What version to set in `install_requires` when a `python_distribution` depends on "
"other `python_distribution`s. If `exact`, will use `==`. If `compatible`, will "
"use `~=`. If `any`, will leave off the version. See "
"https://www.python.org/dev/peps/pep-0440/#version-specifiers."
),
)

def first_party_dependency_version(self, version: str) -> str:
"""Return the version string (e.g. '~=4.0') for a first-party dependency.
If the user specified to use "any" version, then this will return an empty string.
"""
scheme = self.options.first_party_dependency_version_scheme
scheme = self.first_party_dependency_version_scheme
if scheme == FirstPartyDependencyVersionScheme.ANY:
return ""
specifier = "==" if scheme == FirstPartyDependencyVersionScheme.EXACT else "~="
Expand Down
40 changes: 13 additions & 27 deletions src/python/pants/backend/python/subsystems/python_native_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
# Licensed under the Apache License, Version 2.0 (see LICENSE).

import os
from typing import Dict, Tuple
from typing import Dict

from pants.engine.rules import collect_rules
from pants.option.option_types import StrListOption
from pants.option.subsystem import Subsystem
from pants.util.strutil import safe_shlex_join, safe_shlex_split

Expand All @@ -13,32 +14,17 @@ class PythonNativeCode(Subsystem):
options_scope = "python-native-code"
help = "Options for building native code using Python, e.g. when resolving distributions."

@classmethod
def register_options(cls, register):
super().register_options(register)
# TODO(#7735): move the --cpp-flags and --ld-flags to a general subprocess support subsystem.
register(
"--cpp-flags",
type=list,
default=safe_shlex_split(os.environ.get("CPPFLAGS", "")),
advanced=True,
help="Override the `CPPFLAGS` environment variable for any forked subprocesses.",
)
register(
"--ld-flags",
type=list,
default=safe_shlex_split(os.environ.get("LDFLAGS", "")),
advanced=True,
help="Override the `LDFLAGS` environment variable for any forked subprocesses.",
)

@property
def cpp_flags(self) -> Tuple[str, ...]:
return tuple(self.options.cpp_flags)

@property
def ld_flags(self) -> Tuple[str, ...]:
return tuple(self.options.ld_flags)
# TODO(#7735): move the --cpp-flags and --ld-flags to a general subprocess support subsystem.
cpp_flags = StrListOption(
"--cpp-flags",
default=safe_shlex_split(os.environ.get("CPPFLAGS", "")),
help="Override the `CPPFLAGS` environment variable for any forked subprocesses.",
).advanced()
ld_flags = StrListOption(
"--ld-flags",
default=safe_shlex_split(os.environ.get("LDFLAGS", "")),
help="Override the `LDFLAGS` environment variable for any forked subprocesses.",
).advanced()

@property
def environment_dict(self) -> Dict[str, str]:
Expand Down
51 changes: 18 additions & 33 deletions src/python/pants/backend/python/subsystems/repos.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

from __future__ import annotations

from typing import Iterator, cast
from typing import Iterator

from pants.option.option_types import StrListOption
from pants.option.subsystem import Subsystem


Expand All @@ -17,38 +18,22 @@ class PythonRepos(Subsystem):

pypi_index = "https://pypi.org/simple/"

@classmethod
def register_options(cls, register):
super().register_options(register)
register(
"--repos",
advanced=True,
type=list,
default=[],
help=(
"URLs of code repositories to look for requirements. In Pip and Pex, this option "
"corresponds to the `--find-links` option."
),
)
register(
"--indexes",
advanced=True,
type=list,
default=[cls.pypi_index],
help=(
"URLs of code repository indexes to look for requirements. If set to an empty "
"list, then Pex will use no indices (meaning it will not use PyPI). The values "
"should be compliant with PEP 503."
),
)

@property
def repos(self) -> list[str]:
return cast("list[str]", self.options.repos)

@property
def indexes(self) -> list[str]:
return cast("list[str]", self.options.indexes)
repos = StrListOption(
"--repos",
help=(
"URLs of code repositories to look for requirements. In Pip and Pex, this option "
"corresponds to the `--find-links` option."
),
).advanced()
indexes = StrListOption(
"--indexes",
default=[pypi_index],
help=(
"URLs of code repository indexes to look for requirements. If set to an empty "
"list, then Pex will use no indices (meaning it will not use PyPI). The values "
"should be compliant with PEP 503."
),
).advanced()

@property
def pex_args(self) -> Iterator[str]:
Expand Down
Loading

0 comments on commit 7e31f4b

Please sign in to comment.