Repository: mpeteuil/poetry-dotenv-plugin Branch: main Commit: 75a6989512f2 Files: 9 Total size: 11.5 KB Directory structure: gitextract_xbolhul7/ ├── .github/ │ └── workflows/ │ ├── build.yml │ └── release.yml ├── LICENSE ├── README.md ├── poetry_dotenv_plugin/ │ ├── __init__.py │ └── dotenv_plugin.py ├── pyproject.toml └── tests/ ├── __init__.py └── test_system.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/build.yml ================================================ # Heavily based on the Poetry main Github Actions workflow # https://github.com/python-poetry/poetry/blob/1.2.0a1/.github/workflows/main.yml name: "CI" on: push: branches: - main pull_request: branches: - '**' jobs: tests: name: ${{ matrix.os }} / ${{ matrix.python-version }} runs-on: ${{ matrix.os }}-latest strategy: matrix: os: [Ubuntu, MacOS] python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] defaults: run: shell: bash steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 id: setup-python with: python-version: ${{ matrix.python-version }} - name: Install poetry run: | if [[ "${{ matrix.python-version }}" == "3.7" ]]; then curl -sSL https://install.python-poetry.org | python - --version 1.5.1 -y else curl -sSL https://install.python-poetry.org | python - -y fi - name: Update PATH run: echo "$HOME/.local/bin" >> $GITHUB_PATH - name: Configure poetry run: poetry config virtualenvs.in-project true - name: Set up cache uses: actions/cache@v3 id: cache with: path: .venv key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} - name: Ensure cache is healthy if: steps.cache.outputs.cache-hit == 'true' run: timeout 10s poetry run pip --version || rm -rf .venv - name: Install dependencies run: poetry install --no-root - name: Install poetry-dotenv-plugin run: poetry self add "$GITHUB_WORKSPACE" - name: Run system tests run: tests/test_system.sh ================================================ FILE: .github/workflows/release.yml ================================================ # Based on # https://github.com/python-poetry/poetry/blob/a0cc7d6b9ea9b59203ac01e4ac641643dc7c9c7a/.github/workflows/release.yml name: Release on: push: tags: - '*.*.*' jobs: release: name: Release runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python 3.11 uses: actions/setup-python@v4 with: python-version: "3.11" - name: Install Poetry run: curl -sSL https://install.python-poetry.org | python - -y - name: Update PATH run: echo "$HOME/.local/bin" >> $GITHUB_PATH - name: Build project for distribution run: poetry build - name: Check Version id: check-version run: | [[ "$(poetry version --short)" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] \ || echo "prerelease=true" >> "$GITHUB_OUTPUT" - name: Create Release uses: ncipollo/release-action@v1 with: artifacts: "dist/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false prerelease: steps.check-version.outputs.prerelease == 'true' - name: Publish to PyPI env: POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }} run: poetry publish ================================================ FILE: LICENSE ================================================ Copyright (c) 2021 Michael Peteuil Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Poetry Dotenv Plugin [![CI](https://github.com/mpeteuil/poetry-dotenv-plugin/actions/workflows/build.yml/badge.svg)](https://github.com/mpeteuil/poetry-dotenv-plugin/actions/workflows/build.yml) A [Poetry](https://python-poetry.org/) plugin that automatically loads environment variables from `.env` files into the environment before poetry commands are run. Supports Python 3.7+[^1] ```sh $ cat .env MY_ENV_VAR='Hello World' $ poetry run python -c 'import os; print(os.environ.get("MY_ENV_VAR"))' Hello World ``` This plugin depends on the [`python-dotenv` package](https://github.com/theskumar/python-dotenv) for its functionality and therefore also supports features that `python-dotenv` supports. Interpolating variables using POSIX variable expansion for example. ### Origins Initial implementation based on the event handler application plugin example in the [Poetry docs](https://python-poetry.org/docs/plugins/#event-handler). ## Install ```sh poetry self add poetry-dotenv-plugin ``` ### Coming from Pipenv If you are transitioning from `pipenv` there shouldn't be much to change with regard to the `.env` loading. If you were a user of [`pipenv`'s environment variables](https://pipenv.pypa.io/en/latest/advanced/#automatic-loading-of-env) to control `.env` loading then you can use the analogous environment variables listed below. Pipenv env var | Poetry env var -------------- | ---------------------- PIPENV_DOTENV_LOCATION | POETRY_DOTENV_LOCATION PIPENV_DONT_LOAD_ENV | POETRY_DONT_LOAD_ENV ### Overriding existing environment variables By default, this plugin will override existing environment variables. This is because this plugin was built to make onboarding for users coming from `pipenv` as seamless as possible. If you want to prevent existing environment variables from being overridden, you can set the `POETRY_DOTENV_DONT_OVERRIDE` environment variable to `true`.[^2] [^1]: Python 3.7 is supported only when using Poetry < [1.6.0](https://python-poetry.org/history/#160---2023-08-20), which [dropped support for Python 3.7](https://github.com/python-poetry/poetry/pull/7674). [^2]: See [#16](https://github.com/mpeteuil/poetry-dotenv-plugin/pull/16) for background. ================================================ FILE: poetry_dotenv_plugin/__init__.py ================================================ ================================================ FILE: poetry_dotenv_plugin/dotenv_plugin.py ================================================ import os from cleo.events.console_events import COMMAND import dotenv from poetry.console.application import Application from poetry.console.commands.env_command import EnvCommand from poetry.plugins.application_plugin import ApplicationPlugin class DotenvPlugin(ApplicationPlugin): def activate(self, application): application.event_dispatcher.add_listener(COMMAND, self.load_dotenv) def load_dotenv( self, event, event_name, dispatcher ): POETRY_DONT_LOAD_ENV = bool(os.environ.get("POETRY_DONT_LOAD_ENV")) command = event.command if not isinstance(command, EnvCommand) or POETRY_DONT_LOAD_ENV: return POETRY_DOTENV_LOCATION = os.environ.get("POETRY_DOTENV_LOCATION") io = event.io if io.is_debug(): io.write_line("Loading environment variables.") path = POETRY_DOTENV_LOCATION or dotenv.find_dotenv(usecwd=True) POETRY_DOTENV_DONT_OVERRIDE = os.environ.get("POETRY_DOTENV_DONT_OVERRIDE", "") DOTENV_OVERRIDE = not POETRY_DOTENV_DONT_OVERRIDE.lower() in ( "true", "1", ) dotenv.load_dotenv(dotenv_path=path, override=DOTENV_OVERRIDE) ================================================ FILE: pyproject.toml ================================================ [tool.poetry] name = "poetry-dotenv-plugin" version = "0.2.0" description = "A Poetry plugin to automatically load environment variables from .env files" authors = ["Michael Peteuil "] license = "MIT" readme = "README.md" packages = [{include = "poetry_dotenv_plugin"}] homepage = "https://github.com/mpeteuil/poetry-dotenv-plugin" repository = "https://github.com/mpeteuil/poetry-dotenv-plugin" keywords = ["poetry", "poetry-plugin", "plugin", "dotenv"] classifiers = [ "Topic :: Software Development", "Topic :: System :: Systems Administration", "Topic :: Utilities", ] [tool.poetry.dependencies] python = ">=3.7,<4" poetry = ">=1.2.0a1" python-dotenv = ">=0.10.0" [tool.poetry.group.dev.dependencies] pytest = "^6.2.3" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry.plugins."poetry.application.plugin"] poetry-dotenv-plugin = "poetry_dotenv_plugin.dotenv_plugin:DotenvPlugin" ================================================ FILE: tests/__init__.py ================================================ ================================================ FILE: tests/test_system.sh ================================================ #!/usr/bin/env bash function create_dotenv_file() { # Setup .env file so the plugin can do its job echo "export MY_ENV_VAR='foo'" > .env } function delete_dotenv_file() { # Tear down .env file since we no longer need it rm -f .env } function test_end_to_end_system_with_default_dotenv_file() { # Setup local expected expected='foo' create_dotenv_file local output output=$(poetry run python -c "import os; print(os.environ['MY_ENV_VAR'])") # Cleanup delete_dotenv_file if [ "$expected" = "$output" ]; then printf "test_end_to_end_system_with_default_dotenv_file: PASSED\n" else printf "Expected '$expected', but got '%s'.\n" "$output" exit 1 fi } function test_end_to_end_system_with_dotenv_location_override() { # Setup local expected expected='bar' local new_dotenv_path new_dotenv_path="$PWD/tests/tmp" create_dotenv_file # Override the file that was just created mkdir -p "$new_dotenv_path" && echo "export MY_ENV_VAR='bar'" > "$new_dotenv_path/.env" local output output=$(export POETRY_DOTENV_LOCATION="$new_dotenv_path/.env" && poetry run python -c "import os; print(os.environ['MY_ENV_VAR'])") # Cleanup rm -rf "$new_dotenv_path" delete_dotenv_file if [ "$expected" = "$output" ]; then printf "test_end_to_end_system_with_dotenv_location_override: PASSED\n" else printf "Expected '$expected', but got '%s'.\n" "$output" exit 1 fi } function test_end_to_end_system_with_default_env_overrides() { # Setup local expected expected='foo' create_dotenv_file local output output=$(export MY_ENV_VAR='bar' && poetry run python -c "import os; print(os.environ['MY_ENV_VAR'])") # Cleanup delete_dotenv_file if [ "$expected" = "$output" ]; then printf "test_end_to_end_system_with_default_env_overrides: PASSED\n" else printf "Expected '$expected', but got '%s'.\n" "$output" exit 1 fi } function test_end_to_end_system_without_env_overrides() { # Setup local expected expected='bar' create_dotenv_file local output output=$(export MY_ENV_VAR='bar' POETRY_DOTENV_DONT_OVERRIDE=true && poetry run python -c "import os; print(os.environ['MY_ENV_VAR'])") # Cleanup delete_dotenv_file if [ "$expected" = "$output" ]; then printf "test_end_to_end_system_without_env_overrides: PASSED\n" else printf "Expected '$expected', but got '%s'.\n" "$output" exit 1 fi } function test_end_to_end_system_without_loading_dotenv_file() { # Setup local expected expected='Nonexistent Variable' create_dotenv_file local output output=$(export POETRY_DONT_LOAD_ENV=true && poetry run python -c "import os; print(os.environ.get('MY_ENV_VAR', '$expected'))") # Cleanup delete_dotenv_file if [ "$expected" = "$output" ]; then printf "test_end_to_end_system_without_loading_dotenv_file: PASSED\n" else printf "Expected '$expected', but got '%s'.\n" "$output" exit 1 fi } test_end_to_end_system_with_default_dotenv_file test_end_to_end_system_with_dotenv_location_override test_end_to_end_system_with_default_env_overrides test_end_to_end_system_without_env_overrides test_end_to_end_system_without_loading_dotenv_file