# env


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

This module answers two questions every release step runs into: where a
project’s tokens and settings are kept, and what environment a child
command should run with.

An environment value is any named setting a build or a deploy needs,
such as `GITHUB_TOKEN` or `PORT`. Some of them are secrets that should
not sit in plain text.
[`EnvStore`](https://vedicreader.github.io/pullup/env.html#envstore)
keeps them through dockeasy, which puts secrets in the system keychain
and every value in an env file (`~/.config/fastops/.env` unless you name
another). dockeasy is imported the first time a value is read or
written, not when this module loads.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/pullup/blob/main/pullup/env.py#L15"
target="_blank" style="float:right; font-size:smaller">source</a>

### EnvError

``` python
def EnvError(
    *args, **kwargs
):
```

*Unspecified run-time error.*

When pullup runs inside a frozen desktop app, one built with py2app or a
similar tool, the app’s launcher sets variables such as `PYTHONHOME`
that point Python at the copy bundled inside the app. A child process
that inherits them starts the wrong interpreter. `BUNDLE_ONLY` lists
those variables.
[`EnvError`](https://vedicreader.github.io/pullup/env.html#enverror) is
raised when a value cannot be stored, or when dockeasy, which does the
storing, is not installed.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/pullup/blob/main/pullup/env.py#L25"
target="_blank" style="float:right; font-size:smaller">source</a>

### clean_env

``` python
def clean_env():
```

*A copy of this process’s environment with the frozen-app interpreter
variables removed.*

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/pullup/blob/main/pullup/env.py#L18"
target="_blank" style="float:right; font-size:smaller">source</a>

### strip_bundle

``` python
def strip_bundle(
    env, frozen:NoneType=None
):
```

*Remove the frozen-app interpreter variables from `env` when running
frozen, and return it.*

[`strip_bundle`](https://vedicreader.github.io/pullup/env.html#strip_bundle)
removes the `BUNDLE_ONLY` variables from an environment mapping and sets
`PYTHONUTF8=1`. It changes the mapping you pass and returns that same
mapping. By default it only does this when the current process is
frozen; pass `frozen=True` or `frozen=False` to decide yourself, as the
example does.
[`clean_env`](https://vedicreader.github.io/pullup/env.html#clean_env)
covers the common case: a copy of this process’s environment with the
bundle variables always removed.

``` python
env = {'PYTHONHOME': '/Pullup.app/Contents/Resources', 'PATH': '/usr/bin', 'HOME': '/Users/me'}
strip_bundle(dict(env), frozen=True)
```

    {'PATH': '/usr/bin', 'HOME': '/Users/me', 'PYTHONUTF8': '1'}

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/pullup/blob/main/pullup/env.py#L30"
target="_blank" style="float:right; font-size:smaller">source</a>

### venv_env

``` python
def venv_env(
    python:NoneType=None, env:NoneType=None
):
```

*A copy of `env` that runs commands in the virtual environment of
`python`, or unchanged when `python` is None.*

[`venv_env`](https://vedicreader.github.io/pullup/env.html#venv_env)
returns an environment that runs commands inside a particular virtual
environment, much as `source .venv/bin/activate` would. Pass the path of
that environment’s Python. It copies the mapping, sets `VIRTUAL_ENV`,
puts the interpreter’s directory first on `PATH`, and drops `PYTHONHOME`
and `UV_PROJECT_ENVIRONMENT`, either of which would send commands to a
different interpreter. With no `python` the mapping comes back as a
copy, passed only through
[`strip_bundle`](https://vedicreader.github.io/pullup/env.html#strip_bundle).

``` python
e = venv_env('/repo/.venv/bin/python', env={'PATH': '/usr/bin', 'PYTHONHOME': '/frozen'})
e['VIRTUAL_ENV'], e['PATH'], 'PYTHONHOME' in e
```

    ('/repo/.venv', '/repo/.venv/bin:/usr/bin', False)

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/pullup/blob/main/pullup/env.py#L53"
target="_blank" style="float:right; font-size:smaller">source</a>

### needs_extra

``` python
def needs_extra(
    what
):
```

*An error message for `what` that ends with the command that installs
the missing library.*

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/pullup/blob/main/pullup/env.py#L49"
target="_blank" style="float:right; font-size:smaller">source</a>

### extra

``` python
def extra():
```

*The package name that missing-library errors tell the reader to
install.*

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/pullup/blob/main/pullup/env.py#L44"
target="_blank" style="float:right; font-size:smaller">source</a>

### use_extra

``` python
def use_extra(
    name
):
```

*Set the package name that missing-library errors tell the reader to
install.*

Some of pullup’s features need another library, and the error raised
when it is missing ends with the command that installs it.
[`use_extra`](https://vedicreader.github.io/pullup/env.html#use_extra)
changes the package named in that command. A host application that
bundles pullup calls it so that the message names the host’s own
package.
[`needs_extra`](https://vedicreader.github.io/pullup/env.html#needs_extra)
builds the message and
[`extra`](https://vedicreader.github.io/pullup/env.html#extra) returns
the name in use.

[`_dockeasy`](https://vedicreader.github.io/pullup/env.html#_dockeasy)
imports the four dockeasy functions the store uses, or raises
[`EnvError`](https://vedicreader.github.io/pullup/env.html#enverror)
with the install hint. Some dockeasy versions return coroutines from
these functions, so
[`_call`](https://vedicreader.github.io/pullup/env.html#_call) runs
whatever comes back to completion. It works whether or not an event loop
is already running, which is the case inside Jupyter, and it re-raises
any exception the call raised.

``` python
async def double(x): return x*2
_call(double, 21), _call(lambda x: x*2, 21)
```

    (42, 42)

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/pullup/blob/main/pullup/env.py#L82"
target="_blank" style="float:right; font-size:smaller">source</a>

### EnvStore

``` python
def EnvStore(
    service:str='fastops', path:NoneType=None
):
```

*Read and write one project’s tokens and settings through dockeasy’s
keychain and env file.*

[`EnvStore`](https://vedicreader.github.io/pullup/env.html#envstore)
reads and writes the values for one project.

- `get` asks dockeasy for the key. For a secret dockeasy checks the
  keychain, then this process’s environment, then the env file; for an
  ordinary value it skips the keychain. A key found nowhere returns
  `''`.
- `set` with `secret=True`, the default, writes the value to the
  keychain under `service` and to the env file. With `secret=False` it
  writes to the env file only. Either way dockeasy also puts it in
  `os.environ`.
- `unset` removes a key from the keychain, the env file and
  `os.environ`, and reports which of the first two held it.
- `values` reads several keys at once and leaves out the ones with no
  value. This is what a pipeline hands to a child process.

``` python
store = EnvStore(path=envfile)
store.set('PULLUP_DEMO_REGISTRY', 'ghcr.io/example', secret=False)
store.get('PULLUP_DEMO_REGISTRY', secret=False)
```

    'ghcr.io/example'

The examples use `secret=False`, so nothing touches the keychain.

``` python
store.values(['PULLUP_DEMO_REGISTRY', 'PULLUP_DEMO_TAG'], secret=False)
```

    {'PULLUP_DEMO_REGISTRY': 'ghcr.io/example'}

``` python
store.unset('PULLUP_DEMO_REGISTRY')
```

    {'key': 'PULLUP_DEMO_REGISTRY', 'removed': ['env file']}

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/pullup/blob/main/pullup/env.py#L128"
target="_blank" style="float:right; font-size:smaller">source</a>

### env_value

``` python
def env_value(
    store, key, default:str='', secret:bool=False
):
```

*One value from an environment store, or `default` when it is unset or
the store raises.*

[`env_value`](https://vedicreader.github.io/pullup/env.html#env_value)
is the forgiving read. It returns `default` when the key is unset and
also when the store raises, for example because the keychain is locked.
Unlike
[`EnvStore.get`](https://vedicreader.github.io/pullup/env.html#envstore.get),
it reads ordinary values unless you pass `secret=True`. Use it wherever
a missing value has a sensible fallback.

``` python
env_value(store, 'PULLUP_DEMO_REGISTRY', 'ghcr.io/fallback')
```

    'ghcr.io/fallback'
