diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..cf5d05d --- /dev/null +++ b/.flake8 @@ -0,0 +1,9 @@ +[flake8] +# E203 -> whitespace before ':' (conflicts with Black) +# E231 -> Bad trailing comma (conflicts with Black) +# E501 -> line too long (conflicts with Black) + +extend-ignore: + E203, + E231, + E501, diff --git a/.github/workflows/pants.yaml b/.github/workflows/pants.yaml new file mode 100644 index 0000000..ceb31af --- /dev/null +++ b/.github/workflows/pants.yaml @@ -0,0 +1,69 @@ +# Copyright 2020 Pants project contributors. +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +# See https://www.pantsbuild.org/stable/docs/using-pants/using-pants-in-ci for tips on how to set up your CI with Pants. + +name: Pants + +on: [push, pull_request] + +jobs: + org-check: + name: Check GitHub Organization + if: ${{ github.repository_owner == 'pantsbuild' }} + runs-on: ubuntu-24.04 + steps: + - name: Noop + run: "true" + build: + name: Perform CI Checks + needs: org-check + runs-on: ubuntu-24.04 + strategy: + matrix: + python-version: [3.12] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - uses: pantsbuild/actions/init-pants@v8 + # This action bootstraps pants and manages 2-3 GHA caches. + # See: github.com/pantsbuild/actions/tree/main/init-pants/ + with: + # v0 makes it easy to bust the cache if needed + # just increase the integer to start with a fresh cache + gha-cache-key: v0 + # The Python backend uses named_caches for Pip/PEX state, + # so it is appropriate to invalidate on lockfile changes. + named-caches-hash: ${{ hashFiles('python-default.lock') }} + # If you're not using a fine-grained remote caching service (see https://www.pantsbuild.org/docs/remote-caching), + # then you may also want to preserve the local Pants cache (lmdb_store). However this must invalidate for + # changes to any file that can affect the build, so may not be practical in larger repos. + # A remote cache service integrates with Pants's fine-grained invalidation and avoids these problems. + cache-lmdb-store: 'true' # defaults to 'false' + # Note that named_caches and lmdb_store falls back to partial restore keys which + # may give a useful partial result that will save time over completely clean state, + # but will cause the cache entry to grow without bound over time. + # See https://www.pantsbuild.org/stable/docs/using-pants/using-pants-in-ci for tips on how to periodically clean it up. + # Alternatively you change gha-cache-key to ignore old caches. + - name: Check BUILD files + run: | + pants tailor --check update-build-files --check :: + - name: Lint and typecheck + run: | + pants lint check :: + - name: Test + run: | + pants test :: + - name: Package / Run + run: | + # We also smoke test that our release process will work by running `package`. + pants package :: + pants run helloworld/:pex_binary + - name: Upload pants log + uses: actions/upload-artifact@v4 + with: + name: pants-log + path: .pants.d/workdir/pants.log + if: always() # We want the log even on failures. diff --git a/.gitignore b/.gitignore index eb1c004..08c35f4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,141 +2,16 @@ # Licensed under the Apache License, Version 2.0 (see LICENSE). # Pants workspace files -.pants.d/ -dist -.pids -.pants.workdir.file_lock* +/.pants.d/ +/dist +/.pids +/.pants.workdir.file_lock* -# Byte-compiled / optimized / DLL files +# Python files __pycache__/ -*.py[cod] -*$py.class +*.pyc +.venv/ -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# IDEA project files +# Editors .idea/ *.iml diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..282c3fb --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,11 @@ +[settings] +# This is to make isort compatible with Black. See +# https://black.readthedocs.io/en/stable/the_black_code_style.html#how-black-wraps-lines. +line_length=88 +multi_line_output=3 +include_trailing_comma=True +force_grid_wrap=0 +use_parentheses=True + +known_first_party=helloworld +default_section=THIRDPARTY diff --git a/BUILD b/BUILD index 5f81ec9..fca77e4 100644 --- a/BUILD +++ b/BUILD @@ -1,16 +1,7 @@ # Copyright 2020 Pants project contributors. # Licensed under the Apache License, Version 2.0 (see LICENSE). - -# A macro that turns every entry in this directory's requirements.txt into a target -# with the name of the corresponding dist. -# -# For example, `translate>=3.2.1` expands to: -# python_requirement_library( -# name='translate', -# requirements=[ -# python_requirement('translate>=3.2.1') -# ] -# ) - -python_requirements() +# A macro that turns every entry in this directory's requirements.txt into a +# `python_requirement_library` target. Refer to +# https://www.pantsbuild.org/docs/python-third-party-dependencies. +python_requirements(name="reqs") diff --git a/README.md b/README.md index eb32329..3cc3c1d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,188 @@ # example-python -An example repo to demonstrate Python support in Pants v2. +An example repository to demonstrate Python support in Pants. + +See [pantsbuild.org](https://www.pantsbuild.org/docs) for much more detailed documentation. + +This is only one possible way of laying out your project with Pants. See +[pantsbuild.org/docs/source-roots#examples](https://www.pantsbuild.org/docs/source-roots#examples) for some other +example layouts. + +# Running Pants + +You run Pants goals using the `pants` launcher binary, which will bootstrap the +version of Pants configured for this repo if necessary. + +See [here](https://www.pantsbuild.org/docs/installation) for how to install the `pants` binary. + +> :question: Running with Apple Silicon and/or macOS? You will want to make changes to the `search_path` and +`interpreter_constraints` values in `pants.toml` before running `pants` - there is guidance in `pants.toml` +for those settings. + +Use `pants --version` to see the version of Pants configured for the repo (which you can also find +in `pants.toml`). + +# Goals + +Pants commands are called _goals_. You can get a list of goals with + +``` +pants help goals +``` + +# Targets + +Targets are a way of setting metadata for some part of your code, such as timeouts for tests and +entry points for binaries. Targets have types like `python_source`, `resources`, and +`pex_binary`. They are defined in `BUILD` files. + +Pants goals can be invoked on targets or directly on source files (which is often more intuitive and convenient). +In the latter case, Pants locates target metadata for the source files as needed. + +## File specifications + +Invoking goals on files is straightforward, e.g., + +``` +pants test helloworld/greet/greeting_test.py +``` + +You can use globs: + +``` +pants lint helloworld/greet/*.py +``` + +But note that these will be expanded by your shell, so this is equivalent to having used + +``` +pants lint helloworld/greet/__init__.py helloworld/greet/greeting.py helloworld/greet/greeting_test.py +``` + +If you want Pants itself to expand the globs (which is sometimes necessary), you must quote them in the shell: + +``` +pants lint 'helloworld/greet/*.py' +``` + +You can run on all changed files: + +``` +pants --changed-since=HEAD lint +``` + +You can run on all changed files, and any of their "dependents": + +``` +pants --changed-since=HEAD --changed-dependents=transitive test +``` + +## Target specifications + +Targets are referenced on the command line using their address, of the form `path/to/dir:name`, e.g., + +``` +pants lint helloworld/greet:lib +``` + +You can glob over all targets in a directory with a single trailing `:`, or over all targets in a directory +and all its subdirectories with a double trailing `::`, e.g., + +``` +pants lint helloworld:: +``` + +## Globbing semantics + +When you glob over files or targets, Pants knows to ignore ones that aren't relevant to the requested goal. +For example, if you run the `test` goal over a set of files that includes non-test files, Pants will just ignore +those, rather than error. So you can safely do things like + +``` +pants test :: +``` + +To run all tests. + +# Example Goals + +Try these out in this repo! + +## List targets + +``` +pants list :: # All targets. +pants list 'helloworld/**/*.py' # Just targets containing Python code. +``` + +## Run linters and formatters + +``` +pants lint :: +pants fmt helloworld/greet:: +``` + +## Run MyPy + +``` +pants check :: +``` + +## Run tests + +``` +pants test :: # Run all tests in the repo. +pants test --output=all :: # Run all tests in the repo and view pytest output even for tests that passed (you can set this permanently in pants.toml). +pants test helloworld/translator:tests # Run all the tests in this target. +pants test helloworld/translator/translator_test.py # Run just the tests in this file. +pants test helloworld/translator/translator_test.py -- -k test_unknown_phrase # Run just this one test by passing through pytest args. +``` + +## Create a PEX binary + +The `package` goal requires specifying a target which can be packaged. In this case, the there is a `pex_binary` target with the name `pex_binary` in the `helloworld/BUILD` file. + +``` +pants package helloworld:pex_binary +``` + +The pex file is output to `dist/helloworld/pex_binary.pex` and can be executed directly. + +## Run a binary directly + +``` +pants run helloworld/main.py +``` + +## Open a REPL + +``` +pants repl helloworld/greet:lib # The REPL will have all relevant code and dependencies on its sys.path. +pants repl --shell=ipython helloworld/greet:lib --no-pantsd # To use IPython, you must disable Pantsd for now. +``` + +## Build a wheel / generate `setup.py` + +This will build both a `.whl` bdist and a `.tar.gz` sdist. + +``` +pants package helloworld/translator:dist +``` + +## Count lines of code + +``` +pants count-loc '**/*' +``` + +## Generate or update a lockfile containing the dependencies + +``` +pants generate-lockfiles --resolve=python-default +``` + + +## Create virtualenv for IDE integration + +``` +pants export --resolve=python-default +``` diff --git a/get-pants.sh b/get-pants.sh new file mode 100755 index 0000000..cae178e --- /dev/null +++ b/get-pants.sh @@ -0,0 +1,221 @@ +#!/usr/bin/env bash +# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +set -euo pipefail + +COLOR_RED="\x1b[31m" +COLOR_GREEN="\x1b[32m" +COLOR_YELLOW="\x1b[33m" +COLOR_RESET="\x1b[0m" + +function log() { + echo -e "$@" 1>&2 +} + +function die() { + (($# > 0)) && log "${COLOR_RED}$*${COLOR_RESET}" + exit 1 +} + +function green() { + (($# > 0)) && log "${COLOR_GREEN}$*${COLOR_RESET}" +} + +function warn() { + (($# > 0)) && log "${COLOR_YELLOW}$*${COLOR_RESET}" +} + +function check_cmd() { + local cmd="$1" + command -v "$cmd" > /dev/null || die "This script requires the ${cmd} binary to be on the PATH." +} + +help_url="https://www.pantsbuild.org/docs/getting-help" + +_GC=() + +function gc() { + if (($# > 0)); then + check_cmd rm + _GC+=("$@") + else + rm -rf "${_GC[@]}" + fi +} + +trap gc EXIT + +check_cmd uname + +function calculate_os() { + local os + + os="$(uname -s)" + if [[ "${os}" =~ [Ll]inux ]]; then + echo linux + elif [[ "${os}" =~ [Dd]arwin ]]; then + echo macos + elif [[ "${os}" =~ [Ww]in|[Mm][Ii][Nn][Gg] ]]; then + # Powershell reports something like: Windows_NT + # Git bash reports something like: MINGW64_NT-10.0-22621 + echo windows + else + die "Pants is not supported on this operating system (${os}). Please reach out to us at ${help_url} for help." + fi +} + +OS="$(calculate_os)" + +check_cmd basename +if [[ "${OS}" == "windows" ]]; then + check_cmd pwsh +else + check_cmd curl +fi + +function fetch() { + local url="$1" + local dest_dir="$2" + + local dest + dest="${dest_dir}/$(basename "${url}")" + + if [[ "${OS}" == "windows" ]]; then + pwsh -c "Invoke-WebRequest -OutFile $dest -Uri $url" + else + curl --proto '=https' --tlsv1.2 -sSfL -o "${dest}" "${url}" + fi +} + +if [[ "${OS}" == "macos" ]]; then + check_cmd shasum +else + check_cmd sha256sum +fi + +function sha256() { + if [[ "${OS}" == "macos" ]]; then + shasum --algorithm 256 "$@" + else + sha256sum "$@" + fi +} + +check_cmd mktemp + +function install_from_url() { + local url="$1" + local dest="$2" + + local workdir + workdir="$(mktemp -d)" + gc "${workdir}" + + fetch "${url}.sha256" "${workdir}" + fetch "${url}" "${workdir}" + ( + cd "${workdir}" + sha256 -c --status ./*.sha256 || + die "Download from ${url} did not match the fingerprint at ${url}.sha256" + ) + rm "${workdir}/"*.sha256 + if [[ "${OS}" == "macos" ]]; then + mkdir -p "$(dirname "${dest}")" + install -m 755 "${workdir}/"* "${dest}" + else + install -D -m 755 "${workdir}/"* "${dest}" + fi +} + +function calculate_arch() { + local arch + + arch="$(uname -m)" + if [[ "${arch}" =~ x86[_-]64 ]]; then + echo x86_64 + elif [[ "${arch}" =~ arm64|aarch64 ]]; then + echo aarch64 + else + die "Pants is not supported for this chip architecture (${arch}). Please reach out to us at ${help_url} for help." + fi +} + +check_cmd cat + +function usage() { + cat << EOF +Usage: $0 + +Installs the pants launcher binary. + +You only need to run this once on a machine when you do not have "pants" +available to run yet. + +The pants binary takes care of managing and running the underlying +Pants version configured in "pants.toml" in the surrounding Pants-using +project. + +Once installed, if you want to update your "pants" launcher binary, use +"SCIE_BOOT=update pants" to get the latest release or +"SCIE_BOOT=update pants --help" to learn more options. + +-h | --help: Print this help message. + +-d | --bin-dir: + The directory to install the scie-pants binary in, "~/.local/bin" by default. + +-b | --base-name: + The name to use for the scie-pants binary, "pants" by default. + +-V | --version: + The version of the scie-pants binary to install, the latest version by default. + The available versions can be seen at: + https://github.com/pantsbuild/scie-pants/releases + +EOF +} + +bin_dir="${HOME}/.local/bin" +base_name="pants" +version="latest/download" +while (($# > 0)); do + case "$1" in + --help | -h) + usage + exit 0 + ;; + --bin-dir | -d) + bin_dir="$2" + shift + ;; + --base-name | -b) + base_name="$2" + shift + ;; + --version | -V) + version="download/v$2" + shift + ;; + *) + usage + die "Unexpected argument $1\n" + ;; + esac + shift +done + +ARCH="$(calculate_arch)" +URL="https://github.com/pantsbuild/scie-pants/releases/${version}/scie-pants-${OS}-${ARCH}" +dest="${bin_dir}/${base_name}" + +log "Downloading and installing the pants launcher ..." +install_from_url "${URL}" "${dest}" +green "Installed the pants launcher from ${URL} to ${dest}" +if ! command -v "${base_name}" > /dev/null; then + warn "${dest} is not on the PATH." + log "You'll either need to invoke ${dest} explicitly or else add ${bin_dir} to your shell's PATH." +fi + +green "\nRunning \`pants\` in a Pants-enabled repo will use the version of Pants configured for that repo." +green "In a repo not yet Pants-enabled, it will prompt you to set up Pants for that repo." diff --git a/helloworld/BUILD b/helloworld/BUILD index 90b7ba5..5e55812 100644 --- a/helloworld/BUILD +++ b/helloworld/BUILD @@ -1,25 +1,16 @@ # Copyright 2020 Pants project contributors. # Licensed under the Apache License, Version 2.0 (see LICENSE). - -python_binary( - # name defaults to the name of this directory, i.e., `helloworld`. - sources = ["main.py"], - entry_point = "helloworld.main", - dependencies = [ - "//:ansicolors", - "helloworld/greet", - ] +# This target sets the metadata for all the Python non-test files in this directory. +python_sources( + name="lib", ) - -python_binary( - name = "helloworld_py2", - sources = ["main_py2.py"], - entry_point = "helloworld.main_py2", - compatibility = ">=2.7,<3", - dependencies = [ - "//:ansicolors", - "helloworld/greet_py2", - ] +# This target allows us to bundle our app into a PEX binary file via +# `pants package`. We can also run it with `pants run`. See +# https://www.pantsbuild.org/docs/python-package-goal and +# https://www.pantsbuild.org/docs/python-run-goal. +pex_binary( + name="pex_binary", + entry_point="main.py", ) diff --git a/helloworld/__init__.py b/helloworld/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/helloworld/greet/BUILD b/helloworld/greet/BUILD index afecbdb..6dcc65e 100644 --- a/helloworld/greet/BUILD +++ b/helloworld/greet/BUILD @@ -1,29 +1,22 @@ # Copyright 2020 Pants project contributors. # Licensed under the Apache License, Version 2.0 (see LICENSE). - -python_library( - # name defaults to the name of this directory, i.e., `greet`. - # sources defaults to ['*.py', '!*_test.py', '!test_*.py', '!conftest.py']. - dependencies = [ - "//:translate", - ":greetings", - "helloworld/util" - ], - compatibility = ">=3.6", +# This target sets the metadata for all the Python non-test files in this directory. +# +# * Pants cannot yet infer dependencies on `resource` / `resources` targets, so we explicitly add +# the dep. +python_sources( + name="lib", + dependencies=[":translations"], ) - +# This target sets the metadata for all the Python test files in this directory. python_tests( - name = 'tests', - # sources defaults to ['*_test.py', 'test_*.py', 'conftest.py']. - dependencies = [ - ":greet" - ] + name="tests", ) - -resources( - name = 'greetings', - sources = ['greetings.txt'], +# This target teaches Pants about our JSON file, which allows other targets to depend on it. +resource( + name="translations", + source="translations.json", ) diff --git a/helloworld/greet/__init__.py b/helloworld/greet/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/helloworld/greet/greeting.py b/helloworld/greet/greeting.py index e07aff9..70c7675 100644 --- a/helloworld/greet/greeting.py +++ b/helloworld/greet/greeting.py @@ -1,30 +1,29 @@ # Copyright 2020 Pants project contributors. # Licensed under the Apache License, Version 2.0 (see LICENSE). +from __future__ import annotations + +import importlib.resources +import json import random -from helloworld.util.lang import LanguageTranslator -from helloworld.util.resources import get_lines +from helloworld.translator.translator import LanguageTranslator class Greeter: - def __init__(self): - self._greeting_selector = GreetingSelector() - self._language_translator = LanguageTranslator() - - def translated_greeting(self) -> str: - return self._language_translator.translate_to_random_language( - self._greeting_selector.pick_greeting() + def __init__( + self, *, translations: dict[str, dict[str, str]] | None = None + ) -> None: + self._translations = ( + translations + if translations is not None + else json.loads( + importlib.resources.read_text(__name__, "translations.json") + ) ) + self._translator = LanguageTranslator(self._translations) def greet(self, name: str) -> str: - greeting = self.translated_greeting() - return f"{greeting}, {name}!" - - -class GreetingSelector: - def __init__(self): - self._greetings = get_lines(__name__, "greetings.txt") - - def pick_greeting(self): - return random.choice(self._greetings) + random_greeting = random.choice(list(self._translations.keys())) + greeting = self._translator.translate_to_random_language(random_greeting) + return f"{greeting}, {name}!".capitalize() diff --git a/helloworld/greet/greeting_test.py b/helloworld/greet/greeting_test.py index 54cf0ba..8d43373 100644 --- a/helloworld/greet/greeting_test.py +++ b/helloworld/greet/greeting_test.py @@ -4,7 +4,6 @@ from helloworld.greet.greeting import Greeter -def test_greeter(): - greeter = Greeter() - greeting = greeter.greet("world") - assert greeting.endswith("world!") +def test_greeter() -> None: + greeter = Greeter(translations={"hello": {"es": "hola"}}) + assert greeter.greet("test") == "Hola, test!" diff --git a/helloworld/greet/greetings.txt b/helloworld/greet/greetings.txt deleted file mode 100644 index ec0ce49..0000000 --- a/helloworld/greet/greetings.txt +++ /dev/null @@ -1,5 +0,0 @@ -hello -good morning -good afternoon -good evening -salutations diff --git a/helloworld/greet/translations.json b/helloworld/greet/translations.json new file mode 100644 index 0000000..a9eddda --- /dev/null +++ b/helloworld/greet/translations.json @@ -0,0 +1,18 @@ +{ + "hello": { + "es": "hola", + "fr": "bonjour", + "af": "hallo", + "ch": "你好", + "ru": "Привет", + "cs": "ahoj" + }, + "good night": { + "es": "buenas noches", + "fr": "bonne nuit", + "af": "Goeie nag", + "ch": "晚安", + "ru": "спокойной ночи", + "cs": "dobrou noc" + } +} diff --git a/helloworld/greet_py2/BUILD b/helloworld/greet_py2/BUILD deleted file mode 100644 index b06ec05..0000000 --- a/helloworld/greet_py2/BUILD +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright 2020 Pants project contributors. -# Licensed under the Apache License, Version 2.0 (see LICENSE). - - -python_library( - # name defaults to the name of this directory, i.e., `greet_py2`. - # sources defaults to ['*.py', '!*_test.py', '!test_*.py', '!conftest.py']. - compatibility = "CPython>=2.7,<3" -) diff --git a/helloworld/greet_py2/greeting_py2.py b/helloworld/greet_py2/greeting_py2.py deleted file mode 100644 index e15ff30..0000000 --- a/helloworld/greet_py2/greeting_py2.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright 2020 Pants project contributors. -# Licensed under the Apache License, Version 2.0 (see LICENSE). - - -def greet_py2(name): - exec "x = 'hello, {}'.format(name)" - return x diff --git a/helloworld/main.py b/helloworld/main.py index da8b758..125d189 100644 --- a/helloworld/main.py +++ b/helloworld/main.py @@ -2,13 +2,13 @@ # Licensed under the Apache License, Version 2.0 (see LICENSE). from colors import green + from helloworld.greet.greeting import Greeter -def say_hello(): - greeter = Greeter() - sentence = greeter.greet("world") - print(green(sentence)) +def say_hello() -> None: + greeting = Greeter().greet("Pantsbuild") + print(green(greeting)) if __name__ == "__main__": diff --git a/helloworld/main_py2.py b/helloworld/main_py2.py deleted file mode 100644 index 393ff61..0000000 --- a/helloworld/main_py2.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2020 Pants project contributors. -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -from colors import green -from helloworld.greet_py2.greeting_py2 import greet_py2 - - -def say_hello_py2(): - print green(greet_py2("Python 2")) - - -if __name__ == "__main__": - say_hello_py2() diff --git a/helloworld/translator/BUILD b/helloworld/translator/BUILD new file mode 100644 index 0000000..7086f94 --- /dev/null +++ b/helloworld/translator/BUILD @@ -0,0 +1,30 @@ +# Copyright 2020 Pants project contributors. +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +# This target sets the metadata for all the Python non-test files in this directory. +python_sources( + name="lib", +) + +# This target sets the metadata for all the Python test files in this directory. +python_tests( + name="tests", +) + +# This target allows us to build a `.whl` bdist and a `.tar.gz` sdist by auto-generating +# `setup.py`. See https://www.pantsbuild.org/docs/python-distributions. +# +# Because this target has no source code, Pants cannot infer dependencies. We depend on `:lib`, +# which means we'll include all the non-test Python files in this directory, and any of +# their dependencies. +python_distribution( + name="dist", + dependencies=[":lib"], + wheel=True, + sdist=True, + provides=setup_py( + name="helloworld.translator", + version="0.0.1", + description="A language translator.", + ), +) diff --git a/helloworld/translator/__init__.py b/helloworld/translator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/helloworld/translator/translator.py b/helloworld/translator/translator.py new file mode 100644 index 0000000..d9fb1b3 --- /dev/null +++ b/helloworld/translator/translator.py @@ -0,0 +1,46 @@ +# Copyright 2020 Pants project contributors. +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import annotations + +import random +from dataclasses import dataclass + + +class UnknownLanguage(Exception): + pass + + +class UnknownPhrase(Exception): + pass + + +@dataclass +class LanguageTranslator: + """A mapping of phrases (in English) to ISO 639 language codes (like `es`, + `fr`) and their translation. + + Assumes that every phrase is translated into the same languages. + """ + + phrases_to_translations: dict[str, dict[str, str]] + + @property + def all_languages(self) -> set[str]: + return { + lang + for translations in self.phrases_to_translations.values() + for lang in translations.keys() + } + + def translate(self, lang: str, phrase: str) -> str: + if phrase not in self.phrases_to_translations: + raise UnknownPhrase(phrase) + translations = self.phrases_to_translations[phrase] + if lang not in translations: + raise UnknownLanguage(lang) + return translations[lang] + + def translate_to_random_language(self, phrase: str) -> str: + lang = random.choice(sorted(self.all_languages)) + return self.translate(lang, phrase) diff --git a/helloworld/translator/translator_test.py b/helloworld/translator/translator_test.py new file mode 100644 index 0000000..1f838ff --- /dev/null +++ b/helloworld/translator/translator_test.py @@ -0,0 +1,34 @@ +# Copyright 2020 Pants project contributors. +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +import pytest + +from helloworld.translator.translator import ( + LanguageTranslator, + UnknownLanguage, + UnknownPhrase, +) + + +def test_language_translator() -> None: + translator = LanguageTranslator( + { + "hello": {"es": "hola", "ar": "مرحبًا"}, + "computer": {"es": "computadora", "ar": "حاسوب"}, + } + ) + assert translator.translate("es", "hello") == "hola" + assert translator.translate("ar", "hello") == "مرحبًا" + assert translator.translate("es", "computer") == "computadora" + + +def test_unknown_phrase() -> None: + translator = LanguageTranslator({"hello": {"es": "hola"}}) + with pytest.raises(UnknownPhrase): + translator.translate("es", "good morning") + + +def test_unknown_language() -> None: + translator = LanguageTranslator({"hello": {"es": "hola"}}) + with pytest.raises(UnknownLanguage): + translator.translate("ch", "hello") diff --git a/helloworld/util/BUILD b/helloworld/util/BUILD deleted file mode 100644 index 54a0da4..0000000 --- a/helloworld/util/BUILD +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2020 Pants project contributors. -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -python_library( - # name defaults to the name of this directory, i.e., `util`. - # sources defaults to ['*.py', '!*_test.py', '!test_*.py', '!conftest.py']. - dependencies = [ - "//:setuptools", - "//:translate", - ":langs" - ] -) - - -python_tests( - name = 'tests', - # sources defaults to ['*_test.py', 'test_*.py', 'conftest.py']. - dependencies = [ - ":resources_test", - ":util", - ] -) - - -resources( - name = 'langs', - sources = ['langs.txt'], -) - - -resources( - name = 'resources_test', - sources = ['resources_test.txt'], -) diff --git a/helloworld/util/lang.py b/helloworld/util/lang.py deleted file mode 100644 index 9fc775e..0000000 --- a/helloworld/util/lang.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2020 Pants project contributors. -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -import random - -from helloworld.util.resources import get_lines -from translate import Translator - - -class LanguageTranslator: - class UnknownLanguage(Exception): - pass - - def __init__(self): - self._langs = get_lines(__name__, "langs.txt") - - def translate(self, lang: str, phrase: str) -> str: - if lang not in self._langs: - raise self.UnknownLanguage(lang) - translator = Translator(lang) - return translator.translate(phrase) - - def translate_to_random_language(self, phrase: str) -> str: - return self.translate(self._pick_random_language(), phrase) - - def _pick_random_language(self): - return random.choice(self._langs) diff --git a/helloworld/util/lang_test.py b/helloworld/util/lang_test.py deleted file mode 100644 index 83eb6d7..0000000 --- a/helloworld/util/lang_test.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2020 Pants project contributors. -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -import pytest -from helloworld.util.lang import LanguageTranslator - - -def test_language_translator(): - language_translator = LanguageTranslator() - assert "hola" == language_translator.translate("es", "hello") - - -def test_unknown_language(): - with pytest.raises(LanguageTranslator.UnknownLanguage): - LanguageTranslator().translate("xx", "hello") diff --git a/helloworld/util/langs.txt b/helloworld/util/langs.txt deleted file mode 100644 index 6926190..0000000 --- a/helloworld/util/langs.txt +++ /dev/null @@ -1,4 +0,0 @@ -en -de -fr -es diff --git a/helloworld/util/resources.py b/helloworld/util/resources.py deleted file mode 100644 index d33935b..0000000 --- a/helloworld/util/resources.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2020 Pants project contributors. -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -from typing import List - -import pkg_resources - - -def get_lines(pkg_name: str, resource_name: str) -> List[str]: - resource_content = pkg_resources.resource_string(pkg_name, resource_name) - return list(filter(None, resource_content.decode().splitlines())) diff --git a/helloworld/util/resources_test.py b/helloworld/util/resources_test.py deleted file mode 100644 index 367d8c5..0000000 --- a/helloworld/util/resources_test.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright 2020 Pants project contributors. -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -from helloworld.util.resources import get_lines - - -def test_get_lines(): - assert ["banana", "apple", "orange", "pear"] == get_lines( - __name__, "resources_test.txt" - ) diff --git a/helloworld/util/resources_test.txt b/helloworld/util/resources_test.txt deleted file mode 100644 index 9ff41cb..0000000 --- a/helloworld/util/resources_test.txt +++ /dev/null @@ -1,4 +0,0 @@ -banana -apple -orange -pear diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..77ca553 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,31 @@ +[mypy] +# Optionals +no_implicit_optional = True + +# Strictness +allow_untyped_globals = False +allow_redefinition = False +implicit_reexport = False +strict_equality = True + +# Warnings +warn_unused_ignores = True +warn_no_return = True +warn_return_any = True +warn_redundant_casts = True +warn_unreachable = True + +# Error output +show_column_numbers = True +show_error_context = True +show_error_codes = True +show_traceback = True +pretty = True +color_output = True +error_summary = True + +[mypy-colors] +ignore_missing_imports = True + +[mypy-pytest] +ignore_missing_imports = True diff --git a/pants b/pants deleted file mode 100755 index 2e265e6..0000000 --- a/pants +++ /dev/null @@ -1,194 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2020 Pants project contributors. -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -# =============================== NOTE =============================== -# This ./pants bootstrap script comes from the pantsbuild/setup -# project and is intended to be checked into your code repository so -# that any developer can check out your code and be building it with -# Pants with no prior setup needed. -# -# You can learn more here: https://www.pantsbuild.org/install.html -# ==================================================================== - -set -eou pipefail - -PYTHON_BIN_NAME="${PYTHON:-unspecified}" - -PANTS_HOME="${PANTS_HOME:-${XDG_CACHE_HOME:-$HOME/.cache}/pants/setup}" -PANTS_BOOTSTRAP="${PANTS_HOME}/bootstrap-$(uname -s)-$(uname -m)" - -VENV_VERSION=${VENV_VERSION:-16.4.3} - -VENV_PACKAGE=virtualenv-${VENV_VERSION} -VENV_TARBALL=${VENV_PACKAGE}.tar.gz - -COLOR_RED="\x1b[31m" -COLOR_GREEN="\x1b[32m" -COLOR_RESET="\x1b[0m" - -function log() { - echo -e "$@" 1>&2 -} - -function die() { - (($# > 0)) && log "${COLOR_RED}$*${COLOR_RESET}" - exit 1 -} - -function green() { - (($# > 0)) && log "${COLOR_GREEN}$*${COLOR_RESET}" -} - -function tempdir { - mktemp -d "$1"/pants.XXXXXX -} - -function get_exe_path_or_die { - exe="$1" - if ! command -v "${exe}"; then - die "Could not find ${exe}. Please ensure ${exe} is on your PATH." - fi -} - -function get_pants_config_value { - config_key="$1" - optional_space="[[:space:]]*" - if [[ -f 'pants.ini' ]]; then - valid_delimiters="[:=]" - prefix="^${config_key}${optional_space}${valid_delimiters}${optional_space}" - sed -ne "/${prefix}/ s#${prefix}##p" pants.ini && return 0 - fi - if [[ -f 'pants.toml' ]]; then - prefix="^${config_key}${optional_space}=${optional_space}" - raw_value="$(sed -ne "/${prefix}/ s#${prefix}##p" pants.toml)" - echo "${raw_value}" | tr -d \"\' && return 0 - fi - return 0 -} - -function get_python_major_minor_version { - python_exe="$1" - "$python_exe" <&1 > /dev/null)" == "pyenv: python${version}"* ]]; then - continue - fi - echo "${interpreter_path}" && return 0 - done -} - -function determine_python_exe { - if [[ "${PYTHON_BIN_NAME}" != 'unspecified' ]]; then - python_bin_name="${PYTHON_BIN_NAME}" - else - python_bin_name="$(determine_default_python_exe)" - if [[ -z "${python_bin_name}" ]]; then - die "No valid Python interpreter found. Pants requires Python 3.6+ to run." - fi - fi - python_exe="$(get_exe_path_or_die "${python_bin_name}")" - major_minor_version="$(get_python_major_minor_version "${python_exe}")" - for valid_version in '36' '37' '38' '39'; do - if [[ "${major_minor_version}" == "${valid_version}" ]]; then - echo "${python_exe}" && return 0 - fi - done - die "Invalid Python interpreter version for ${python_exe}. Pants requires Python 3.6+ to run." -} - -# TODO(John Sirois): GC race loser tmp dirs leftover from bootstrap_XXX -# functions. Any tmp dir w/o a symlink pointing to it can go. - -function bootstrap_venv { - if [[ ! -d "${PANTS_BOOTSTRAP}/${VENV_PACKAGE}" ]]; then - ( - mkdir -p "${PANTS_BOOTSTRAP}" - staging_dir=$(tempdir "${PANTS_BOOTSTRAP}") - cd "${staging_dir}" - curl -LO "https://pypi.io/packages/source/v/virtualenv/${VENV_TARBALL}" - tar -xzf "${VENV_TARBALL}" - ln -s "${staging_dir}/${VENV_PACKAGE}" "${staging_dir}/latest" - mv "${staging_dir}/latest" "${PANTS_BOOTSTRAP}/${VENV_PACKAGE}" - ) 1>&2 - fi - echo "${PANTS_BOOTSTRAP}/${VENV_PACKAGE}" -} - -function bootstrap_pants { - pants_version="$1" - python="$2" - - pants_requirement="pantsbuild.pants==${pants_version}" - python_major_minor_version="$(get_python_major_minor_version "${python}")" - target_folder_name="${pants_version}_py${python_major_minor_version}" - - if [[ ! -d "${PANTS_BOOTSTRAP}/${target_folder_name}" ]]; then - ( - venv_path="$(bootstrap_venv)" - staging_dir=$(tempdir "${PANTS_BOOTSTRAP}") - "${python}" "${venv_path}/virtualenv.py" --no-download "${staging_dir}/install" - "${staging_dir}/install/bin/pip" install -U pip - "${staging_dir}/install/bin/pip" install "${pants_requirement}" - ln -s "${staging_dir}/install" "${staging_dir}/${target_folder_name}" - mv "${staging_dir}/${target_folder_name}" "${PANTS_BOOTSTRAP}/${target_folder_name}" - green "New virtual environment successfully created at ${PANTS_BOOTSTRAP}/${target_folder_name}." - ) 1>&2 - fi - echo "${PANTS_BOOTSTRAP}/${target_folder_name}" -} - -# Ensure we operate from the context of the ./pants buildroot. -cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" -pants_version="$(determine_pants_version)" -python="$(determine_python_exe)" -pants_dir="$(bootstrap_pants "${pants_version}" "${python}")" - - -# We set the env var no_proxy to '*', to work around an issue with urllib using non -# async-signal-safe syscalls after we fork a process that has already spawned threads. -# -# See https://blog.phusion.nl/2017/10/13/why-ruby-app-servers-break-on-macos-high-sierra-and-what-can-be-done-about-it/ -export no_proxy='*' - -exec "${pants_dir}/bin/python" "${pants_dir}/bin/pants" "$@" diff --git a/pants.ci.toml b/pants.ci.toml new file mode 100644 index 0000000..bcf7a2e --- /dev/null +++ b/pants.ci.toml @@ -0,0 +1,15 @@ +# Copyright 2020 Pants project contributors. +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +# See https://www.pantsbuild.org/docs/using-pants-in-ci. + +[GLOBAL] +dynamic_ui = false +colors = true + +# Set to `true` if you have a Pants remote cache provider available for CI builds +remote_cache_read = false +remote_cache_write = false + +[pytest] +args = ["-vv", "--no-header"] diff --git a/pants.toml b/pants.toml index c4a7325..9c62618 100644 --- a/pants.toml +++ b/pants.toml @@ -2,38 +2,45 @@ # Licensed under the Apache License, Version 2.0 (see LICENSE). [GLOBAL] -pants_version = "1.27.0.dev0" -v1 = false # Turn off the v1 execution engine. -v2 = true # Enable the v2 execution engine. -v2_ui = true # Enable the v2 execution engine's terminal-based UI. - -backend_packages = [] # Deregister all v1 backends. - -# List v2 backends here. -backend_packages2 = [ - 'pants.backend.python', - 'pants.backend.python.lint.docformatter', - 'pants.backend.python.lint.black', - 'pants.backend.python.lint.flake8', - 'pants.backend.python.lint.isort', - 'pants.backend.python.lint.pylint', +pants_version = "2.29.0" +backend_packages.add = [ + "pants.backend.build_files.fmt.black", + "pants.backend.python", + "pants.backend.python.lint.docformatter", + "pants.backend.python.lint.black", + "pants.backend.python.lint.flake8", + "pants.backend.python.lint.isort", + "pants.backend.python.typecheck.mypy", ] -# List v2 plugins here. -plugins2 = [] - +[anonymous-telemetry] +enabled = true +repo_id = "3B1D361B-E9F1-49A8-B761-03DCC41FD58E" [source] -# The python source root is the repo root. -source_roots = """{ - '': ('python',), -}""" - - -[python-setup] -# The default interpreter compatibility for code in this repo. -# Individual targets can override this with the `compatibility` field. -# For example, targets containing Python 2.7-only code can set `compatibility = "CPython>=2.7,<3"`, -# and targets containing code compatible with Python 2.7+ *and* Python 3.5+ can set -# `compatibility = CPython>=2.7,!=3.0,!=3.1,!=3.2,!=3.3,!=3.4`. -interpreter_constraints = "CPython>=3.7" +# The Python source root is the repo root. See https://www.pantsbuild.org/docs/source-roots. +root_patterns = ["/"] + +[python] +# The default interpreter constraints for code in this repo. Individual targets can override +# this with the `interpreter_constraints` field. See +# https://www.pantsbuild.org/docs/python-interpreter-compatibility. +# +# Modify this if you don't have Python 3.13 on your machine. +# This can be a range, such as [">=3.8,<3.11"], but it's usually recommended to restrict +# to a single minor version. +interpreter_constraints = ["==3.12.*"] + +# Enable the "resolves" mechanism, which turns on lockfiles for user code. See +# https://www.pantsbuild.org/docs/python-third-party-dependencies. This also adds the +# `generate-lockfiles` goal for Pants to generate the lockfile for you. +enable_resolves = true + +resolves = { python-default = "python-default.lock"} + +[python-bootstrap] +# We search for interpreters both on the $PATH and in the `$(pyenv root)/versions` folder. +# If you're using macOS, you may want to leave off the entry to avoid using the +# problematic system Pythons. See +# https://www.pantsbuild.org/docs/python-interpreter-compatibility#changing-the-interpreter-search-path. +search_path = ["", ""] diff --git a/pants_from_sources b/pants_from_sources deleted file mode 100755 index 08de322..0000000 --- a/pants_from_sources +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2020 Pants project contributors. -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -# Runs pants from sources. Useful for debugging. -# Assumes you have the pantsbuild/pants repo checked out in a sibling dir of this dir, -# named 'pants' but overridable using PANTS_SOURCE. - -set -euo pipefail - -cd "$(git rev-parse --show-toplevel)" - -PANTS_SOURCE="${PANTS_SOURCE:-../pants}" - -# When running pants from sources you are likely to be modifying those sources, so -# you won't want pantsd running. You can override this by setting ENABLE_PANTSD=true. -ENABLE_PANTSD="${ENABLE_PANTSD:-false}" - -backend_packages=( -) - -pythonpath=( -) - -plugins=( -) - -function string_list() { - eval local -r list_variable="\${$1[@]}" - - echo -n "[" - for item in ${list_variable}; do - echo -n "\"${item}\"," - done - echo -n "]" -} - -export PANTS_VERSION="$(cat "${PANTS_SOURCE}/src/python/pants/VERSION")" -export PANTS_PLUGINS2="$(string_list plugins)" -export PANTS_PYTHONPATH="+$(string_list pythonpath)" -export PANTS_BACKEND_PACKAGES2="+$(string_list backend_packages)" -export PANTS_ENABLE_PANTSD="${ENABLE_PANTSD}" -export no_proxy="*" - -exec "${PANTS_SOURCE}/pants" "--no-verify-config" "$@" diff --git a/python-default.lock b/python-default.lock new file mode 100644 index 0000000..4f755f2 --- /dev/null +++ b/python-default.lock @@ -0,0 +1,375 @@ +// This lockfile was autogenerated by Pants. To regenerate, run: +// +// pants generate-lockfiles --resolve=python-default +// +// --- BEGIN PANTS LOCKFILE METADATA: DO NOT EDIT OR REMOVE --- +// { +// "version": 4, +// "valid_for_interpreter_constraints": [ +// "CPython==3.12.*" +// ], +// "generated_with_requirements": [ +// "ansicolors==1.1.8", +// "pytest==7.1.3", +// "setuptools<57,>=56.2.0", +// "types-setuptools<58,>=56.2.0" +// ], +// "manylinux": "manylinux2014", +// "requirement_constraints": [], +// "only_binary": [], +// "no_binary": [], +// "excludes": [], +// "overrides": [] +// } +// --- END PANTS LOCKFILE METADATA --- + +{ + "allow_builds": true, + "allow_prereleases": false, + "allow_wheels": true, + "build_isolation": true, + "constraints": [], + "elide_unused_requires_dist": false, + "excluded": [], + "locked_resolves": [ + { + "locked_requirements": [ + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "00d2dde5a675579325902536738dd27e4fac1fd68f773fe36c21044eb559e187", + "url": "https://files.pythonhosted.org/packages/53/18/a56e2fe47b259bb52201093a3a9d4a32014f9d85071ad07e9d60600890ca/ansicolors-1.1.8-py2.py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "99f94f5e3348a0bcd43c82e5fc4414013ccc19d70bd939ad71e0133ce9c372e0", + "url": "https://files.pythonhosted.org/packages/76/31/7faed52088732704523c259e24c26ce6f2f33fbeff2ff59274560c27628e/ansicolors-1.1.8.zip" + } + ], + "project_name": "ansicolors", + "requires_dists": [], + "requires_python": null, + "version": "1.1.8" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", + "url": "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", + "url": "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz" + } + ], + "project_name": "attrs", + "requires_dists": [ + "cloudpickle; platform_python_implementation == \"CPython\" and extra == \"benchmark\"", + "cloudpickle; platform_python_implementation == \"CPython\" and extra == \"cov\"", + "cloudpickle; platform_python_implementation == \"CPython\" and extra == \"dev\"", + "cloudpickle; platform_python_implementation == \"CPython\" and extra == \"tests\"", + "cogapp; extra == \"docs\"", + "coverage[toml]>=5.3; extra == \"cov\"", + "furo; extra == \"docs\"", + "hypothesis; extra == \"benchmark\"", + "hypothesis; extra == \"cov\"", + "hypothesis; extra == \"dev\"", + "hypothesis; extra == \"tests\"", + "mypy>=1.11.1; (platform_python_implementation == \"CPython\" and python_version >= \"3.10\") and extra == \"benchmark\"", + "mypy>=1.11.1; (platform_python_implementation == \"CPython\" and python_version >= \"3.10\") and extra == \"cov\"", + "mypy>=1.11.1; (platform_python_implementation == \"CPython\" and python_version >= \"3.10\") and extra == \"dev\"", + "mypy>=1.11.1; (platform_python_implementation == \"CPython\" and python_version >= \"3.10\") and extra == \"tests\"", + "mypy>=1.11.1; (platform_python_implementation == \"CPython\" and python_version >= \"3.10\") and extra == \"tests-mypy\"", + "myst-parser; extra == \"docs\"", + "pre-commit-uv; extra == \"dev\"", + "pympler; extra == \"benchmark\"", + "pympler; extra == \"cov\"", + "pympler; extra == \"dev\"", + "pympler; extra == \"tests\"", + "pytest-codspeed; extra == \"benchmark\"", + "pytest-mypy-plugins; (platform_python_implementation == \"CPython\" and python_version >= \"3.10\") and extra == \"benchmark\"", + "pytest-mypy-plugins; (platform_python_implementation == \"CPython\" and python_version >= \"3.10\") and extra == \"cov\"", + "pytest-mypy-plugins; (platform_python_implementation == \"CPython\" and python_version >= \"3.10\") and extra == \"dev\"", + "pytest-mypy-plugins; (platform_python_implementation == \"CPython\" and python_version >= \"3.10\") and extra == \"tests\"", + "pytest-mypy-plugins; (platform_python_implementation == \"CPython\" and python_version >= \"3.10\") and extra == \"tests-mypy\"", + "pytest-xdist[psutil]; extra == \"benchmark\"", + "pytest-xdist[psutil]; extra == \"cov\"", + "pytest-xdist[psutil]; extra == \"dev\"", + "pytest-xdist[psutil]; extra == \"tests\"", + "pytest>=4.3.0; extra == \"benchmark\"", + "pytest>=4.3.0; extra == \"cov\"", + "pytest>=4.3.0; extra == \"dev\"", + "pytest>=4.3.0; extra == \"tests\"", + "sphinx-notfound-page; extra == \"docs\"", + "sphinx; extra == \"docs\"", + "sphinxcontrib-towncrier; extra == \"docs\"", + "towncrier; extra == \"docs\"" + ], + "requires_python": ">=3.8", + "version": "25.3.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", + "url": "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", + "url": "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz" + } + ], + "project_name": "iniconfig", + "requires_dists": [], + "requires_python": ">=3.8", + "version": "2.1.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", + "url": "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", + "url": "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz" + } + ], + "project_name": "packaging", + "requires_dists": [], + "requires_python": ">=3.8", + "version": "25.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", + "url": "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", + "url": "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz" + } + ], + "project_name": "pluggy", + "requires_dists": [ + "coverage; extra == \"testing\"", + "pre-commit; extra == \"dev\"", + "pytest-benchmark; extra == \"testing\"", + "pytest; extra == \"testing\"", + "tox; extra == \"dev\"" + ], + "requires_python": ">=3.9", + "version": "1.6.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378", + "url": "https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", + "url": "https://files.pythonhosted.org/packages/98/ff/fec109ceb715d2a6b4c4a85a61af3b40c723a961e8828319fbcb15b868dc/py-1.11.0.tar.gz" + } + ], + "project_name": "py", + "requires_dists": [], + "requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7", + "version": "1.11.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7", + "url": "https://files.pythonhosted.org/packages/e3/b9/3541bbcb412a9fd56593005ff32183825634ef795a1c01ceb6dee86e7259/pytest-7.1.3-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39", + "url": "https://files.pythonhosted.org/packages/a4/a7/8c63a4966935b0d0b039fd67ebf2e1ae00f1af02ceb912d838814d772a9a/pytest-7.1.3.tar.gz" + } + ], + "project_name": "pytest", + "requires_dists": [ + "argcomplete; extra == \"testing\"", + "attrs>=19.2.0", + "colorama; sys_platform == \"win32\"", + "hypothesis>=3.56; extra == \"testing\"", + "importlib-metadata>=0.12; python_version < \"3.8\"", + "iniconfig", + "mock; extra == \"testing\"", + "nose; extra == \"testing\"", + "packaging", + "pluggy<2.0,>=0.12", + "py>=1.8.2", + "pygments>=2.7.2; extra == \"testing\"", + "requests; extra == \"testing\"", + "tomli>=1.0.0", + "xmlschema; extra == \"testing\"" + ], + "requires_python": ">=3.7", + "version": "7.1.3" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "bc30153eec47d82f20c6f5e1a13d4ee725c6deb7013a67557f89bfe5d25235c4", + "url": "https://files.pythonhosted.org/packages/d0/15/5041473f5d142ee93bf1593deb8f932e27a078f6f04e2020cf44044f72c5/setuptools-56.2.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "7bb5652625e94e73b9358b7ed8c6431b732e80cf31f4e0972294c64f0e5b849e", + "url": "https://files.pythonhosted.org/packages/fc/0a/b486efab52f8ad03c3eca0c998dd3deafba0c39b29e0c49c68a7152c8b2d/setuptools-56.2.0.tar.gz" + } + ], + "project_name": "setuptools", + "requires_dists": [ + "certifi==2016.9.26; extra == \"certs\"", + "flake8-2020; extra == \"testing\"", + "jaraco.envs; extra == \"testing\"", + "jaraco.packaging>=8.2; extra == \"docs\"", + "jaraco.path>=3.2.0; extra == \"testing\"", + "mock; extra == \"testing\"", + "paver; extra == \"testing\"", + "pip>=19.1; extra == \"testing\"", + "pygments-github-lexers==0.0.5; extra == \"docs\"", + "pytest-black>=0.3.7; (platform_python_implementation != \"PyPy\" and python_version < \"3.10\") and extra == \"testing\"", + "pytest-checkdocs>=2.4; extra == \"testing\"", + "pytest-cov; extra == \"testing\"", + "pytest-enabler>=1.0.1; extra == \"testing\"", + "pytest-flake8; extra == \"testing\"", + "pytest-mypy; (platform_python_implementation != \"PyPy\" and python_version < \"3.10\") and extra == \"testing\"", + "pytest-virtualenv>=1.2.7; extra == \"testing\"", + "pytest-xdist; extra == \"testing\"", + "pytest>=4.6; extra == \"testing\"", + "rst.linker>=1.9; extra == \"docs\"", + "sphinx-inline-tabs; extra == \"docs\"", + "sphinx; extra == \"docs\"", + "sphinx; extra == \"testing\"", + "virtualenv>=13.0.0; extra == \"testing\"", + "wheel; extra == \"testing\"", + "wincertstore==0.2; sys_platform == \"win32\" and extra == \"ssl\"" + ], + "requires_python": ">=3.6", + "version": "56.2.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", + "url": "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", + "url": "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", + "url": "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz" + }, + { + "algorithm": "sha256", + "hash": "4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", + "url": "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", + "url": "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", + "url": "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", + "url": "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", + "url": "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", + "url": "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", + "url": "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + } + ], + "project_name": "tomli", + "requires_dists": [], + "requires_python": ">=3.8", + "version": "2.2.1" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "9660b8774b12cd61b448e2fd87a667c02e7ec13ce9f15171f1d49a4654c4df6a", + "url": "https://files.pythonhosted.org/packages/14/45/b8368a8c2d1dc4fa47eb4db980966e23edecbda16fab7a38186b076bbd4d/types_setuptools-57.4.18-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "8ee03d823fe7fda0bd35faeae33d35cb5c25b497263e6a58b34c4cfd05f40bcf", + "url": "https://files.pythonhosted.org/packages/13/5e/3d46cd143913bd51dde973cd23b1d412de9662b08a3b8c213f26b265e6f1/types-setuptools-57.4.18.tar.gz" + } + ], + "project_name": "types-setuptools", + "requires_dists": [], + "requires_python": null, + "version": "57.4.18" + } + ], + "platform_tag": null + } + ], + "only_builds": [], + "only_wheels": [], + "overridden": [], + "path_mappings": {}, + "pex_version": "2.55.2", + "pip_version": "24.2", + "prefer_older_binary": false, + "requirements": [ + "ansicolors==1.1.8", + "pytest==7.1.3", + "setuptools<57,>=56.2.0", + "types-setuptools<58,>=56.2.0" + ], + "requires_python": [ + "CPython==3.12.*" + ], + "resolver_version": "pip-2020-resolver", + "style": "universal", + "target_systems": [ + "linux", + "mac" + ], + "transitive": true, + "use_pep517": null, + "use_system_time": false +} diff --git a/requirements.txt b/requirements.txt index adf9bcc..979167d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ # Copyright 2020 Pants project contributors. # Licensed under the Apache License, Version 2.0 (see LICENSE). -ansicolors>=1.0.2 -setuptools>=42.0.0 -translate>=3.2.1 +ansicolors==1.1.8 +setuptools>=56.2.0,<57 +types-setuptools>=56.2.0,<58 +pytest==7.1.3 \ No newline at end of file