File: test_parameter_decorator.py

package info (click to toggle)
python-cyclopts 3.12.0-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 3,288 kB
  • sloc: python: 11,445; makefile: 24
file content (92 lines) | stat: -rw-r--r-- 3,216 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
from dataclasses import dataclass
from textwrap import dedent
from typing import Annotated, Optional

import pytest
from attrs import define

from cyclopts import Parameter
from cyclopts.exceptions import UnknownOptionError


@pytest.mark.parametrize("decorator", [dataclass, define])
def test_parameter_decorator_dataclass(app, assert_parse_args, decorator):
    @Parameter(name="*")  # Flatten namespace.
    @decorator
    class User:
        name: str
        age: int

    @app.command
    def create(*, user: Optional[User] = None):
        pass

    assert_parse_args(create, "create")
    assert_parse_args(create, "create --name=Bob --age=100", user=User("Bob", 100))  # pyright: ignore[reportCallIssue]


@pytest.mark.parametrize("decorator", [dataclass, define])
def test_parameter_decorator_dataclass_nested_1(app, decorator, console):
    """
    https://github.com/BrianPugh/cyclopts/issues/320
    """

    @decorator
    class S3Path:
        bucket: Annotated[str, Parameter()]
        key: Annotated[str, Parameter()]

    @Parameter(name="*")  # Flatten namespace.
    @decorator
    class S3CliParams:
        path: Annotated[S3Path, Parameter(name="*")]
        region: Annotated[str, Parameter(name="region")]

    @app.command
    def action(*, s3_path: S3CliParams):
        pass

    with console.capture() as capture:
        app("action --help", console=console)

    actual = capture.get()
    expected = dedent(
        """\
        Usage: test_parameter_decorator action [OPTIONS]

        ╭─ Parameters ───────────────────────────────────────────────────────╮
        │ *  --bucket  [required]                                            │
        │ *  --key     [required]                                            │
        │ *  --region  [required]                                            │
        ╰────────────────────────────────────────────────────────────────────╯
        """
    )
    assert actual == expected


def test_parameter_decorator_dataclass_inheritance(app, assert_parse_args):
    @Parameter(name="person")  # Should override the name="u" below.
    @Parameter(name="u", negative_bool=[])
    @dataclass
    class User:
        name: str
        age: int
        privileged: bool = False

    @Parameter(name="a", negative_bool=None)  # Should revert to Cyclopts defaults
    @dataclass
    class Admin(User):
        privileged: bool = True

    @app.command
    def create(*, user: Optional[User] = None, admin: Optional[Admin] = None):
        pass

    assert_parse_args(create, "create --person.name=Bob --person.age=100", user=User("Bob", 100))
    with pytest.raises(UnknownOptionError):
        app("create --person.no-privileged", exit_on_error=False)

    assert_parse_args(create, "create --a.name=Bob --a.age=100", admin=Admin("Bob", 100))
    assert_parse_args(
        create, "create --a.name=Bob --a.age=100 --a.no-privileged", admin=Admin("Bob", 100, privileged=False)
    )