Skip to content

Commit

Permalink
feat: Add ability to directly open file handles.
Browse files Browse the repository at this point in the history
  • Loading branch information
DanCardin committed Feb 8, 2024
1 parent 7d2df6b commit 37e3423
Show file tree
Hide file tree
Showing 12 changed files with 435 additions and 53 deletions.
105 changes: 65 additions & 40 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,184 +1,209 @@
# Changelog

## 0.15.4
## 0.16

### 0.16.0

- feat: Add support for `BinaryIO` and `TextIO` for representing preconfigured
file objects to be returned the caller.

## 0.15

### 0.15.4

- feat: Support pydantic v1.

## 0.15.3
### 0.15.3

- fix: Incorrect error message when using an invalid base class type.

## 0.15.2
### 0.15.2

- fix: Process `action` inference, taking into account `Optional`/`| None`.

## 0.15.1
### 0.15.1

- feat: Support explicit context managers as invoke dependencies.

## 0.15.0
### 0.15.0

- feat: Add docutils directive extension.

## 0.14.3
## 0.14

### 0.14.3

- fix: Handle TypeError in mapping failures

## 0.14.2
### 0.14.2

- fix: Default bool fields to `False` when omitted.

## 0.14.1
### 0.14.1

- fix: zsh completion script error

## 0.14.0
### 0.14.0

- feat: Support functions as interface for simple CLIs.

## 0.13.2
## 0.13

### 0.13.2

- Support "discriminated unions" (i.e. unions which have type distinctions that
affect how they're mapped.)

## 0.13.1
### 0.13.1

- Prefer the field default (if set), if an `Env` is used, but no default is
supplied.

## 0.13.0
### 0.13.0

- Support `yield` in invoke dependencies to support context-manager-like
dependencies (for example those which require cleanup).

## 0.12.1
## 0.12

### 0.12.1

- When used in combination with `parse=...`, handle the "optional" part of
`T | None` **before** `parse`.

## 0.12.0
### 0.12.0

- Add `invoke_async` to support async invoke functions and dependencies

## 0.11.6
## 0.11

### 0.11.6

- Disallow certain combinations of apparently incompatible annotations, i.e.
sequences and scalars

## 0.11.5
### 0.11.5

- Fix double dash following an invalid option (with num_args>0)

## 0.11.4
### 0.11.4

- Fix num_args=-1 on options

## 0.11.3
### 0.11.3

- Continue to parse docstrings without docstring_parser extra
- Fix rendering issue with markdown in docstrings

## 0.11.2
### 0.11.2

- Make docstring_parser dependency optional
- Fix parser error if option followed unknown argument

## 0.11.1
### 0.11.1

- (Hopefully) Configure rich properly to deal with line overflow when printing
terminal escape codes.

## 0.11.0
### 0.11.0

- Add option for explicit Output object, and add `error_format` option to allow
customizing output formatting.

## 0.10.2
## 0.10

### 0.10.2

- Disallow explicit `required=False` in combination with the lack of a field
level default.

## 0.10.1
### 0.10.1

- Fix regression resulting from `value_name`/`field_name` split.

## 0.10.0
### 0.10.0

- Split Arg `value_name`/`field_name`. `value_name` controls help/error output
naming. `field_name` controls the the destination field on the dataclass
object.

## 0.9.3
## 0.9

### 0.9.3

- Ensure output of missing required options is deterministically ordered
- Output all required options when listing out missing required options
- Fix ignore num_args=0 override

## 0.9.2
### 0.9.2

- Invoke the specific callable subcommand instance being targeted.

## 0.9.1
### 0.9.1

- Supply the parsed Command instance as an invoke dependency.

## 0.9.0
### 0.9.0

- Change default backend to `cappa.parser.backend`. To opt into argparse
backend, explicitly supply it with `backend=cappa.argparse.backend`.

## 0.8.9
## 0.8

### 0.8.9

- Avoid mutating command when adding meta arguments
- Avoid setting global NO_COLOR env var when disabling color

## 0.8.8
### 0.8.8

- Clean up help text output formatting
- Show rich-style help text when using argparse backend

## 0.8.7
### 0.8.7

- Allow defining custom callable as an `action`.
- Improve behavior consuming concatenated short arguments

## 0.8.6
### 0.8.6

- Improve behavior consuming concatenated short arguments

## 0.8.5
### 0.8.5

- Add metadata to package distribution

## 0.8.4
### 0.8.4

- Loosen dependency version specifiers

## 0.8.3
### 0.8.3

- Fix `Literal["one", "two"]` syntax vs `Literal["one"] | Literal["two"]`
- Apply custom completions to already "valid" arguments values
- Deduplicate the --completion helptext choices

## 0.8.2
### 0.8.2

- The command's name should now always translate to the prog name
- Explicitly provided argv should now **not** include the prog name

## 0.8.1
### 0.8.1

- Correct the version long name when long=True.

## 0.8.0
### 0.8.0

- Implement support for PEP-727 help text inference.

## 0.7.1
## 0.7

### 0.7.1

- Provide clear error message when a version Arg is supplied without a name.
- Documentation updates

## 0.7.0
### 0.7.0

- Adds native cappa parser option vs argparse
- Renames render option to "backend"
Expand Down
55 changes: 55 additions & 0 deletions docs/source/annotation.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,58 @@ Supplying `foo bar` as the input value should produce `("foo", "bar")`, whereas
`list[...]`, `tuple[...]`, `set[...]` all will coerce the parsed sequences of
values into their corresponding type. The inner type will be mapped for each
item in the sequence.

### `typing.BinaryIO`/`typing.TextIO`

[BinaryIO](typing.BinaryIO) and [TextIO](typing.TextIO) are used to produce an
open file handle to the file path given by the CLI input for that argument.

This can be thought of as equivlent to `open("foo.py")`, given some
`cli --foo foo.py`, which is roughly equivalent to the
[FileType](https://docs.python.org/3/library/argparse.html#argparse.FileType)
feature from `argparse`.

```python
@dataclass
class Args:
foo: typing.BinaryIO


args = cappa.parse(Args)
with args.foo:
print(args.foo.read())
```

```{note}
The supported types do not map to concrete, instantiatable types. This is
important, because neither of these types would otherwise be valid type
annotations in the context of cappa's other inference rules.
It's also important, because there are no concrete types which correspond
to the underlying types returned by `open()`, which would allow the distinction
between binary and text content.
```

#### Controlling `open(...)` options like `mode="w"`

An un-`Annotated` IO type translates to `open(<cli value>)` with no additional
arguments, with the exception that `BinaryIO` infers `mode='b'`.

In order to directly customize arguments like `mode`, `buffering`, `encoding`,
and `errors`, a [FileMode](cappa.FileMode) must be annotated on the input
argument.

```python
import dataclasses
import typing
import cappa

@dataclasses.dataclass
class Args:
foo: typing.Annotated[typing.BinaryIO, cappa.FileMode(mode='wb', encoding='utf-8')]
bar: typing.Annotated[typing.BinaryIO, cappa.Arg(short=True), cappa.FileMode(mode='wb')]
```

As shown, [FileMode](cappa.FileMode) is annotated much like a [Arg](cappa.Arg),
and can be used alongside one depending on the details of the argument in
question.
2 changes: 1 addition & 1 deletion docs/source/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

```{eval-rst}
.. autoapimodule:: cappa
:members: parse, invoke, invoke_async, collect, command, Command, Subcommand, Dep, Arg, ArgAction, Exit, Env, Completion, Output
:members: parse, invoke, invoke_async, collect, command, Command, Subcommand, Dep, Arg, ArgAction, Exit, Env, Completion, Output, FileMode
```

```{eval-rst}
Expand Down
4 changes: 4 additions & 0 deletions docs/source/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
```{include} ../../CHANGELOG.md
:relative-docs: docs/source/
:relative-images:
```
1 change: 1 addition & 0 deletions docs/source/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,5 @@ Internals <internals>
GitHub Repository <https://github.com/dancardin/cappa>
PyPI <https://pypi.org/project/cappa>
Changelog <changelog>
```
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "cappa"
version = "0.15.4"
version = "0.16.0"
description = "Declarative CLI argument parser."

repository = "https://github.com/dancardin/cappa"
Expand Down
2 changes: 2 additions & 0 deletions src/cappa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from cappa.command import Command
from cappa.completion.types import Completion
from cappa.env import Env
from cappa.file_io import FileMode
from cappa.invoke import Dep
from cappa.output import Exit, HelpExit, Output
from cappa.subcommand import Subcommand, Subcommands
Expand All @@ -21,6 +22,7 @@
"Dep",
"Env",
"Exit",
"FileMode",
"HelpExit",
"Output",
"Subcommand",
Expand Down
Loading

0 comments on commit 37e3423

Please sign in to comment.