Esempio n. 1
0
class AvroSubsystem(JvmToolBase):
    options_scope = "java-avro"
    help = "Avro IDL compiler (https://avro.apache.org/)."

    default_version = "1.11.0"
    default_artifacts = ("org.apache.avro:avro-tools:{version}", )
    default_lockfile_resource = (
        "pants.backend.codegen.avro.java",
        "avro-tools.default.lockfile.txt",
    )
    default_lockfile_path = (
        "src/python/pants/backend/codegen/avro/java/avro-tools.default.lockfile.txt"
    )
    default_lockfile_url = git_url(default_lockfile_path)

    runtime_dependencies = TargetListOption(
        "--runtime-dependencies",
        help=lambda cls: softwrap(f"""
            A list of addresses to `jvm_artifact` targets for the runtime dependencies needed for
            generated Java code to work. For example, `['3rdparty/jvm:avro-runtime']`.
            These dependencies will be automatically added to every `avro_sources` target.
            At the very least, this option must be set to a `jvm_artifact` for the
            `org.apache.avro:avro:{cls.default_version}` runtime library.
            """),
    )
Esempio n. 2
0
class ApacheThriftJavaSubsystem(Subsystem):
    options_scope = "java-thrift"
    help = "Options specific to generating Java from Thrift using the Apache Thrift generator"

    gen_options = StrListOption(
        "--options",
        help=softwrap("""
            Code generation options specific to the Java code generator to pass to the
            Apache `thrift` binary via the `-gen java` argument.
            See `thrift -help` for supported values.
            """),
    )
    _runtime_dependencies = TargetListOption(
        "--runtime-dependencies",
        help=softwrap("""
            A list of addresses to `jvm_artifact` targets for the runtime
            dependencies needed for generated Java code to work. For example,
            `['3rdparty/jvm:libthrift']`. These dependencies will
            be automatically added to every `thrift_source` target. At the very least,
            this option must be set to a `jvm_artifact` for the
            `org.apache.thrift:libthrift` runtime library.
            """),
    )

    @property
    def runtime_dependencies(self) -> UnparsedAddressInputs:
        return UnparsedAddressInputs(self._runtime_dependencies,
                                     owning_address=None)
Esempio n. 3
0
class ScalaPBSubsystem(JvmToolBase):
    options_scope = "scalapb"
    help = "The ScalaPB protocol buffer compiler (https://scalapb.github.io/)."

    default_version = "0.11.6"
    default_artifacts = ("com.thesamet.scalapb:scalapbc_2.13:{version}",)
    default_lockfile_resource = (
        "pants.backend.codegen.protobuf.scala",
        "scalapbc.default.lockfile.txt",
    )
    default_lockfile_path = (
        "src/python/pants/backend/codegen/protobuf/scala/scalapbc.default.lockfile.txt"
    )
    default_lockfile_url = git_url(default_lockfile_path)

    _runtime_dependencies = TargetListOption(
        "--runtime-dependencies",
        help=lambda cls: softwrap(
            f"""
            A list of addresses to `jvm_artifact` targets for the runtime
            dependencies needed for generated Scala code to work. For example,
            `['3rdparty/jvm:scalapb-runtime']`. These dependencies will
            be automatically added to every `protobuf_sources` target. At the very least,
            this option must be set to a `jvm_artifact` for the
            `com.thesamet.scalapb:scalapb-runtime_SCALAVER:{cls.default_version}` runtime library.
            """
        ),
    )
    _jvm_plugins = StrListOption(
        "--jvm-plugins",
        help=softwrap(
            """
            A list of JVM-based `protoc` plugins to invoke when generating Scala code from protobuf files.
            The format for each plugin specifier is `NAME=ARTIFACT` where NAME is the name of the
            plugin and ARTIFACT is either the address of a `jvm_artifact` target or the colon-separated
            Maven coordinate for the plugin's jar artifact.

            For example, to invoke the fs2-grpc protoc plugin, the following option would work:
            `--scalapb-jvm-plugins=fs2=org.typelevel:fs2-grpc-codegen_2.12:2.3.1`.
            (Note: you would also need to set --scalapb-runtime-dependencies appropriately
            to include the applicable runtime libraries for your chosen protoc plugins.)
            """
        ),
    )

    @property
    def runtime_dependencies(self) -> UnparsedAddressInputs:
        return UnparsedAddressInputs(self._runtime_dependencies, owning_address=None)

    @property
    def jvm_plugins(self) -> tuple[PluginArtifactSpec, ...]:
        return tuple(PluginArtifactSpec.from_str(pa_str) for pa_str in self._jvm_plugins)
Esempio n. 4
0
class ScroogeJavaSubsystem(Subsystem):
    options_scope = "java-scrooge"
    help = "Java-specific options for the Scrooge Thrift IDL compiler (https://twitter.github.io/scrooge/)."

    _runtime_dependencies = TargetListOption(
        "--runtime-dependencies",
        help=softwrap("""
            A list of addresses to `jvm_artifact` targets for the runtime
            dependencies needed for generated Java code to work. For example,
            `['3rdparty/jvm:libthrift']`. These dependencies will
            be automatically added to every `thrift_source` target. At the very least,
            this option must be set to a `jvm_artifact` for the
            `org.apache.thrift:libthrift` runtime library.
            """),
    )

    @property
    def runtime_dependencies(self) -> UnparsedAddressInputs:
        return UnparsedAddressInputs(self._runtime_dependencies,
                                     owning_address=None)
Esempio n. 5
0
class ScroogeScalaSubsystem(Subsystem):
    options_scope = "scala-scrooge"
    help = "Scala-specific options for the Scrooge Thrift IDL compiler (https://twitter.github.io/scrooge/)."

    _runtime_dependencies = TargetListOption(
        "--runtime-dependencies",
        help=softwrap(f"""
            A list of addresses to `jvm_artifact` targets for the runtime
            dependencies needed for generated Scala code to work. For example,
            `['3rdparty/jvm:scrooge-runtime']`. These dependencies will
            be automatically added to every `thrift_source` target. At the very least,
            this option must be set to a `jvm_artifact` for the
            `com.twitter:scrooge-runtime_SCALAVER:{ScroogeSubsystem.default_version}` runtime library.
            """),
    )

    @property
    def runtime_dependencies(self) -> UnparsedAddressInputs:
        return UnparsedAddressInputs(
            self._runtime_dependencies,
            owning_address=None,
            description_of_origin=
            f"the option `[{self.options_scope}].runtime_dependencies`",
        )
Esempio n. 6
0
class MyPy(PythonToolBase):
    options_scope = "mypy"
    name = "MyPy"
    help = "The MyPy Python type checker (http://mypy-lang.org/)."

    default_version = "mypy==0.910"
    default_main = ConsoleScript("mypy")

    # See `mypy/rules.py`. We only use these default constraints in some situations.
    register_interpreter_constraints = True
    default_interpreter_constraints = ["CPython>=3.7,<4"]

    register_lockfile = True
    default_lockfile_resource = ("pants.backend.python.typecheck.mypy",
                                 "mypy.lock")
    default_lockfile_path = "src/python/pants/backend/python/typecheck/mypy/mypy.lock"
    default_lockfile_url = git_url(default_lockfile_path)
    uses_requirements_from_source_plugins = True

    skip = SkipOption("check")
    args = ArgsListOption(example="--python-version 3.7 --disallow-any-expr")
    config = FileOption(
        "--config",
        default=None,
        advanced=True,
        help=lambda cls:
        ("Path to a config file understood by MyPy "
         "(https://mypy.readthedocs.io/en/stable/config_file.html).\n\n"
         f"Setting this option will disable `[{cls.options_scope}].config_discovery`. Use "
         f"this option if the config is located in a non-standard location."),
    )
    config_discovery = BoolOption(
        "--config-discovery",
        default=True,
        advanced=True,
        help=lambda cls:
        ("If true, Pants will include any relevant config files during "
         "runs (`mypy.ini`, `.mypy.ini`, and `setup.cfg`)."
         f"\n\nUse `[{cls.options_scope}].config` instead if your config is in a "
         f"non-standard location."),
    )
    _source_plugins = TargetListOption(
        "--source-plugins",
        advanced=True,
        help=
        ("An optional list of `python_sources` target addresses to load first-party "
         "plugins.\n\n"
         "You must also set `plugins = path.to.module` in your `mypy.ini`, and "
         "set the `[mypy].config` option in your `pants.toml`.\n\n"
         "To instead load third-party plugins, set the option `[mypy].extra_requirements` "
         "and set the `plugins` option in `mypy.ini`."
         "Tip: it's often helpful to define a dedicated 'resolve' via "
         "`[python].resolves` for your MyPy plugins such as 'mypy-plugins' "
         "so that the third-party requirements used by your plugin, like `mypy`, do not "
         "mix with the rest of your project. Read that option's help message for more info "
         "on resolves."),
    )
    extra_type_stubs = StrListOption(
        "--extra-type-stubs",
        advanced=True,
        help=
        ("Extra type stub requirements to install when running MyPy.\n\n"
         "Normally, type stubs can be installed as typical requirements, such as putting "
         "them in `requirements.txt` or using a `python_requirement` target."
         "Alternatively, you can use this option so that the dependencies are solely "
         "used when running MyPy and are not runtime dependencies.\n\n"
         "Expects a list of pip-style requirement strings, like "
         "`['types-requests==2.25.9']`."),
    )

    @property
    def config_request(self) -> ConfigFilesRequest:
        # Refer to https://mypy.readthedocs.io/en/stable/config_file.html.
        return ConfigFilesRequest(
            specified=self.config,
            specified_option_name=f"{self.options_scope}.config",
            discovery=self.config_discovery,
            check_existence=["mypy.ini", ".mypy.ini"],
            check_content={
                "setup.cfg": b"[mypy",
                "pyproject.toml": b"[tool.mypy"
            },
        )

    @property
    def source_plugins(self) -> UnparsedAddressInputs:
        return UnparsedAddressInputs(self._source_plugins, owning_address=None)

    def check_and_warn_if_python_version_configured(
            self, config: FileContent | None) -> bool:
        """Determine if we can dynamically set `--python-version` and warn if not."""
        configured = []
        if config and b"python_version" in config.content:
            configured.append(
                f"`python_version` in {config.path} (which is used because of either config "
                "discovery or the `[mypy].config` option)")
        if "--py2" in self.args:
            configured.append("`--py2` in the `--mypy-args` option")
        if any(arg.startswith("--python-version") for arg in self.args):
            configured.append("`--python-version` in the `--mypy-args` option")
        if configured:
            formatted_configured = " and you set ".join(configured)
            logger.warning(
                f"You set {formatted_configured}. Normally, Pants would automatically set this "
                "for you based on your code's interpreter constraints "
                f"({doc_url('python-interpreter-compatibility')}). Instead, it will "
                "use what you set.\n\n"
                "(Automatically setting the option allows Pants to partition your targets by their "
                "constraints, so that, for example, you can run MyPy on Python 2-only code and "
                "Python 3-only code at the same time. This feature may no longer work.)"
            )
        return bool(configured)
Esempio n. 7
0
class Pylint(PythonToolBase):
    options_scope = "pylint"
    name = "Pylint"
    help = "The Pylint linter for Python code (https://www.pylint.org/)."

    default_version = "pylint>=2.11.0,<2.12"
    default_main = ConsoleScript("pylint")

    register_lockfile = True
    default_lockfile_resource = ("pants.backend.python.lint.pylint",
                                 "pylint.lock")
    default_lockfile_path = "src/python/pants/backend/python/lint/pylint/pylint.lock"
    default_lockfile_url = git_url(default_lockfile_path)
    uses_requirements_from_source_plugins = True

    skip = SkipOption("lint")
    args = ArgsListOption(
        example="--ignore=foo.py,bar.py --disable=C0330,W0311")
    config = FileOption(
        "--config",
        default=None,
        advanced=True,
        help=lambda cls:
        ("Path to a config file understood by Pylint "
         "(http://pylint.pycqa.org/en/latest/user_guide/run.html#command-line-options).\n\n"
         f"Setting this option will disable `[{cls.options_scope}].config_discovery`. Use "
         f"this option if the config is located in a non-standard location."),
    )
    config_discovery = BoolOption(
        "--config-discovery",
        default=True,
        advanced=True,
        help=lambda cls:
        ("If true, Pants will include any relevant config files during "
         "runs (`.pylintrc`, `pylintrc`, `pyproject.toml`, and `setup.cfg`)."
         f"\n\nUse `[{cls.options_scope}].config` instead if your config is in a "
         f"non-standard location."),
    )
    _source_plugins = TargetListOption(
        "--source-plugins",
        advanced=True,
        help=
        ("An optional list of `python_sources` target addresses to load first-party "
         "plugins.\n\nYou must set the plugin's parent directory as a source root. For "
         "example, if your plugin is at `build-support/pylint/custom_plugin.py`, add "
         "'build-support/pylint' to `[source].root_patterns` in `pants.toml`. This is "
         "necessary for Pants to know how to tell Pylint to discover your plugin. See "
         f"{doc_url('source-roots')}\n\n"
         f"You must also set `load-plugins=$module_name` in your Pylint config file.\n\n"
         "While your plugin's code can depend on other first-party code and third-party "
         "requirements, all first-party dependencies of the plugin must live in the same "
         "directory or a subdirectory.\n\n"
         "To instead load third-party plugins, set the "
         "option `[pylint].extra_requirements` and set the `load-plugins` option in your "
         "Pylint config.\n\n"
         "Tip: it's often helpful to define a dedicated 'resolve' via "
         "`[python].resolves` for your Pylint plugins such as 'pylint-plugins' "
         "so that the third-party requirements used by your plugin, like `pylint`, do not "
         "mix with the rest of your project. Read that option's help message for more info "
         "on resolves."),
    )

    def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest:
        # Refer to http://pylint.pycqa.org/en/latest/user_guide/run.html#command-line-options for
        # how config files are discovered.
        return ConfigFilesRequest(
            specified=self.config,
            specified_option_name=f"[{self.options_scope}].config",
            discovery=self.config_discovery,
            check_existence=[
                ".pylinrc", *(os.path.join(d, "pylintrc") for d in ("", *dirs))
            ],
            check_content={
                "pyproject.toml": b"[tool.pylint]",
                "setup.cfg": b"[pylint."
            },
        )

    @property
    def source_plugins(self) -> UnparsedAddressInputs:
        return UnparsedAddressInputs(self._source_plugins, owning_address=None)
Esempio n. 8
0
class Flake8(PythonToolBase):
    options_scope = "flake8"
    name = "Flake8"
    help = "The Flake8 Python linter (https://flake8.pycqa.org/)."

    default_version = "flake8>=3.9.2,<4.0"
    default_main = ConsoleScript("flake8")

    register_lockfile = True
    default_lockfile_resource = ("pants.backend.python.lint.flake8", "flake8.lock")
    default_lockfile_path = "src/python/pants/backend/python/lint/flake8/flake8.lock"
    default_lockfile_url = git_url(default_lockfile_path)

    skip = SkipOption("lint")
    args = ArgsListOption(example="--ignore E123,W456 --enable-extensions H111")
    config = FileOption(
        "--config",
        default=None,
        advanced=True,
        help=lambda cls: (
            "Path to an INI config file understood by Flake8 "
            "(https://flake8.pycqa.org/en/latest/user/configuration.html).\n\n"
            f"Setting this option will disable `[{cls.options_scope}].config_discovery`. Use "
            f"this option if the config is located in a non-standard location."
        ),
    )
    config_discovery = BoolOption(
        "--config-discovery",
        default=True,
        advanced=True,
        help=lambda cls: (
            "If true, Pants will include any relevant config files during "
            "runs (`.flake8`, `flake8`, `setup.cfg`, and `tox.ini`)."
            f"\n\nUse `[{cls.options_scope}].config` instead if your config is in a "
            f"non-standard location."
        ),
    )
    _source_plugins = TargetListOption(
        "--source-plugins",
        advanced=True,
        help=(
            "An optional list of `python_sources` target addresses to load first-party "
            "plugins.\n\nYou must set the plugin's parent directory as a source root. For "
            "example, if your plugin is at `build-support/flake8/custom_plugin.py`, add "
            "'build-support/flake8' to `[source].root_patterns` in `pants.toml`. This is "
            "necessary for Pants to know how to tell Flake8 to discover your plugin. See "
            f"{doc_url('source-roots')}\n\nYou must also set `[flake8:local-plugins]` in "
            "your Flake8 config file. "
            "For example:\n\n"
            "```\n"
            "[flake8:local-plugins]\n"
            "    extension =\n"
            "        CUSTOMCODE = custom_plugin:MyChecker\n"
            "```\n\n"
            "While your plugin's code can depend on other first-party code and third-party "
            "requirements, all first-party dependencies of the plugin must live in the same "
            "directory or a subdirectory.\n\n"
            "To instead load third-party plugins, set the option "
            "`[flake8].extra_requirements`.\n\n"
            "Tip: it's often helpful to define a dedicated 'resolve' via "
            "`[python].resolves` for your Flake8 plugins such as 'flake8-plugins' "
            "so that the third-party requirements used by your plugin, like `flake8`, do not "
            "mix with the rest of your project. Read that option's help message for more info "
            "on resolves."
        ),
    )

    @property
    def config_request(self) -> ConfigFilesRequest:
        # See https://flake8.pycqa.org/en/latest/user/configuration.html#configuration-locations
        # for how Flake8 discovers config files.
        return ConfigFilesRequest(
            specified=self.config,
            specified_option_name=f"[{self.options_scope}].config",
            discovery=self.config_discovery,
            check_existence=["flake8", ".flake8"],
            check_content={"setup.cfg": b"[flake8]", "tox.ini": b"[flake8]"},
        )

    @property
    def source_plugins(self) -> UnparsedAddressInputs:
        return UnparsedAddressInputs(self._source_plugins, owning_address=None)
Esempio n. 9
0
    class MySubsystem(Subsystem):
        def __init__(self):
            pass

        str_opt = StrOption("--opt", default="", help="")
        optional_str_opt = StrOption("--opt", default=None, help="")
        int_opt = IntOption("--opt", default=0, help="")
        optional_int_opt = IntOption("--opt", default=None, help="")
        float_opt = FloatOption("--opt", default=1.0, help="")
        optional_float_opt = FloatOption("--opt", default=None, help="")
        bool_opt = BoolOption("--opt", default=True, help="")
        optional_bool_opt = BoolOption("--opt", default=None, help="")
        target_opt = TargetOption("--opt", default="", help="")
        optional_target_opt = TargetOption("--opt", default=None, help="")
        dir_opt = DirOption("--opt", default="", help="")
        optional_dir_opt = DirOption("--opt", default=None, help="")
        file_opt = FileOption("--opt", default="", help="")
        optional_file_opt = FileOption("--opt", default=None, help="")
        shellstr_opt = ShellStrOption("--opt", default="", help="")
        optional_shellstr_opt = ShellStrOption("--opt", default=None, help="")
        memorysize_opt = MemorySizeOption("--opt", default=1, help="")
        optional_memorysize_opt = MemorySizeOption("--opt", default=None, help="")

        # List opts
        str_list_opt = StrListOption("--opt", help="")
        int_list_opt = IntListOption("--opt", help="")
        float_list_opt = FloatListOption("--opt", help="")
        bool_list_opt = BoolListOption("--opt", help="")
        target_list_opt = TargetListOption("--opt", help="")
        dir_list_opt = DirListOption("--opt", help="")
        file_list_opt = FileListOption("--opt", help="")
        shellstr_list_opt = ShellStrListOption("--opt", help="")
        memorysize_list_opt = MemorySizeListOption("--opt", help="")
        # And just test one dynamic default
        dyn_str_list_opt = StrListOption("--opt", default=lambda cls: cls.default, help="")

        # Enum opts
        enum_opt = EnumOption("--opt", default=MyEnum.Val1, help="")
        optional_enum_opt = EnumOption("--opt", enum_type=MyEnum, default=None, help="")
        dyn_enum_opt = EnumOption(
            "--opt", enum_type=MyEnum, default=lambda cls: cls.default, help=""
        )
        # mypy correctly complains about not matching any possibilities
        enum_opt_bad = EnumOption("--opt", default=None, help="")  # type: ignore[call-overload]
        enum_list_opt1 = EnumListOption("--opt", default=[MyEnum.Val1], help="")
        enum_list_opt2 = EnumListOption("--opt", enum_type=MyEnum, help="")
        dyn_enum_list_opt = EnumListOption(
            "--opt", enum_type=MyEnum, default=lambda cls: cls.default_list, help=""
        )
        # mypy correctly complains about needing a type annotation
        enum_list_bad_opt = EnumListOption("--opt", default=[], help="")  # type: ignore[var-annotated]

        # Dict opts
        dict_opt1 = DictOption[str]("--opt", help="")
        dict_opt2 = DictOption[Any]("--opt", default=dict(key="val"), help="")
        # mypy correctly complains about needing a type annotation
        dict_opt3 = DictOption("--opt", help="")  # type: ignore[var-annotated]
        dict_opt4 = DictOption("--opt", default={"key": "val"}, help="")
        dict_opt5 = DictOption("--opt", default=dict(key="val"), help="")
        dict_opt6 = DictOption("--opt", default=dict(key=1), help="")
        dict_opt7 = DictOption("--opt", default=dict(key1=1, key2="str"), help="")
        dyn_dict_opt = DictOption[str]("--opt", default=lambda cls: cls.default, help="")

        # Specialized Opts
        skip_opt = SkipOption("fmt")
        args_opt = ArgsListOption(example="--whatever")