asana-api-cli 3.1.0__tar.gz → 3.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {asana_api_cli-3.1.0/src/asana_api_cli.egg-info → asana_api_cli-3.1.1}/PKG-INFO +1 -1
  2. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/pyproject.toml +7 -7
  3. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli/cli.py +4 -4
  4. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli/click_ext.py +2 -2
  5. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli/session.py +1 -1
  6. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli/structured_arg.py +2 -2
  7. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1/src/asana_api_cli.egg-info}/PKG-INFO +1 -1
  8. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/tests/test_cli_invocation.py +10 -3
  9. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/tests/test_sdk_boilerplate.py +17 -9
  10. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/tests/test_session.py +1 -1
  11. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/tests/test_structured_arg.py +2 -2
  12. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/LICENSE +0 -0
  13. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/README.md +0 -0
  14. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/setup.cfg +0 -0
  15. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli/__init__.py +0 -0
  16. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli/formatter.py +0 -0
  17. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli/redactor.py +0 -0
  18. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli/version.py +0 -0
  19. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli.egg-info/SOURCES.txt +0 -0
  20. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli.egg-info/dependency_links.txt +0 -0
  21. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli.egg-info/entry_points.txt +0 -0
  22. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli.egg-info/requires.txt +0 -0
  23. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/src/asana_api_cli.egg-info/top_level.txt +0 -0
  24. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/tests/test_cli.py +0 -0
  25. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/tests/test_cli_surface.py +0 -0
  26. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/tests/test_click_ext.py +0 -0
  27. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/tests/test_formatter.py +0 -0
  28. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/tests/test_py310_compat.py +0 -0
  29. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/tests/test_redactor.py +0 -0
  30. {asana_api_cli-3.1.0 → asana_api_cli-3.1.1}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: asana-api-cli
3
- Version: 3.1.0
3
+ Version: 3.1.1
4
4
  Summary: Command-line wrapper around the official Asana Python SDK
5
5
  Author: Masanao Izumo
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "asana-api-cli"
3
- version = "3.1.0"
3
+ version = "3.1.1"
4
4
  description = "Command-line wrapper around the official Asana Python SDK"
5
5
  authors = [{name = "Masanao Izumo"}]
6
6
  readme = "README.md"
@@ -35,13 +35,13 @@ asana-api = "asana_api_cli.cli:main"
35
35
 
36
36
  [dependency-groups]
37
37
  dev = [
38
- "ruff>=0.6",
39
- "basedpyright>=1.13",
38
+ "ruff>=0.15",
39
+ "basedpyright>=1.39",
40
40
  "pytest>=9,<10",
41
- "build>=1,<2",
42
- "twine>=6,<7",
43
- "vermin>=1.6,<2",
44
- "vcrpy>=6",
41
+ "build>=1.5,<2",
42
+ "twine>=6.2,<7",
43
+ "vermin>=1.8,<2",
44
+ "vcrpy>=8",
45
45
  "pytest-recording>=0.13",
46
46
  ]
47
47
 
@@ -350,14 +350,14 @@ def _escape_help(text: str) -> str:
350
350
  # asana-api label uses parentheses. Six kinds cover the SDK input structure:
351
351
  #
352
352
  # (Configuration: <name>) set on asana.Configuration (global flags)
353
- # (ApiClient: <name>) set on the ApiClient instance (user_agent, default_headers)
353
+ # (ApiClient: <name>) set on the ApiClient instance (user_agent, set_default_header)
354
354
  # (args: <name>) positional method argument (body / path GID / workspace_gid)
355
355
  # (opts: <name>) entry in the method ``opts`` dict (docstring :param)
356
356
  # (kwargs: <name>) boilerplate **kwargs every method accepts (all_params)
357
357
  # (asana-api: extension) no SDK counterpart (CLI-only)
358
358
  #
359
359
  # Configuration globals and the two ApiClient-instance globals (--user-agent /
360
- # --default-header) carry the literal by hand in both ``main`` and
360
+ # --set-default-header) carry the literal by hand in both ``main`` and
361
361
  # ``_make_global_option_params`` (kept byte-identical between cli.py and
362
362
  # click_ext.py by ``test_click_ext.TestHelpTextSync``); the CLI-only formatter
363
363
  # flags (``--output`` / ``--query`` / ``--csv-bom`` and the error-path twins
@@ -1210,7 +1210,7 @@ def _retry_strategy_option(f: Any) -> Any:
1210
1210
  help=("Override the User-Agent header the SDK sends on every request. (ApiClient: user_agent)"),
1211
1211
  )
1212
1212
  @click.option(
1213
- "--default-header",
1213
+ "--set-default-header",
1214
1214
  "default_headers",
1215
1215
  multiple=True,
1216
1216
  callback=default_header_callback,
@@ -1218,7 +1218,7 @@ def _retry_strategy_option(f: Any) -> Any:
1218
1218
  "Add an HTTP header sent on every request, given as NAME=VALUE; "
1219
1219
  "repeatable. Unlike per-call --header-params it applies to all "
1220
1220
  "calls. Not redacted in --debug output — see SECURITY.md. "
1221
- "(ApiClient: default_headers)"
1221
+ "(ApiClient: set_default_header)"
1222
1222
  ),
1223
1223
  )
1224
1224
  @_retry_strategy_option
@@ -166,14 +166,14 @@ def _make_global_option_params() -> list[click.Option]:
166
166
  ),
167
167
  ),
168
168
  click.Option(
169
- ["--default-header", "default_headers"],
169
+ ["--set-default-header", "default_headers"],
170
170
  multiple=True,
171
171
  callback=default_header_callback,
172
172
  help=(
173
173
  "Add an HTTP header sent on every request, given as NAME=VALUE; "
174
174
  "repeatable. Unlike per-call --header-params it applies to all "
175
175
  "calls. Not redacted in --debug output — see SECURITY.md. "
176
- "(ApiClient: default_headers)"
176
+ "(ApiClient: set_default_header)"
177
177
  ),
178
178
  ),
179
179
  *(
@@ -222,7 +222,7 @@ class AsanaSession:
222
222
 
223
223
  # ApiClient-instance settings (not Configuration knobs) applied after
224
224
  # construction. ``default_headers`` first, then ``user_agent`` last, so
225
- # the dedicated ``--user-agent`` wins over a ``--default-header`` that
225
+ # the dedicated ``--user-agent`` wins over a ``--set-default-header`` that
226
226
  # also targets ``User-Agent`` (both write ``default_headers['User-Agent']``).
227
227
  if runtime.default_headers:
228
228
  for name, value in runtime.default_headers.items():
@@ -24,7 +24,7 @@ insensitive). ``1`` / ``0`` are intentionally rejected so int and bool
24
24
  fields cannot be confused by readers of the command line.
25
25
 
26
26
  The module also hosts :func:`default_header_callback`, a separate
27
- ``NAME=VALUE``-per-occurrence parser for the repeatable ``--default-header``
27
+ ``NAME=VALUE``-per-occurrence parser for the repeatable ``--set-default-header``
28
28
  global. It is deliberately not the hybrid parser above: header values often
29
29
  contain commas, which the ``key=value,key=value`` shorthand would mis-split.
30
30
  """
@@ -210,7 +210,7 @@ def click_callback(
210
210
  def default_header_callback(
211
211
  ctx: click.Context, param: click.Parameter, value: tuple[str, ...]
212
212
  ) -> dict[str, str] | None:
213
- """Click ``callback`` for a repeatable ``--default-header NAME=VALUE`` option.
213
+ """Click ``callback`` for a repeatable ``--set-default-header NAME=VALUE`` option.
214
214
 
215
215
  ``multiple=True`` hands the callback a tuple of raw ``NAME=VALUE`` tokens
216
216
  (empty when the flag was not given). Returns ``None`` for "not given" so the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: asana-api-cli
3
- Version: 3.1.0
3
+ Version: 3.1.1
4
4
  Summary: Command-line wrapper around the official Asana Python SDK
5
5
  Author: Masanao Izumo
6
6
  License-Expression: MIT
@@ -510,7 +510,7 @@ class TestRetryStrategyReachesSession:
510
510
 
511
511
 
512
512
  class TestHttpHeaderGlobalsReachClient:
513
- """``--user-agent`` and ``--default-header`` are ApiClient-instance globals;
513
+ """``--user-agent`` and ``--set-default-header`` are ApiClient-instance globals;
514
514
  they must reach the ``ApiClient`` that issues the request, and (like every
515
515
  global) be accepted at the leaf-command level."""
516
516
 
@@ -538,7 +538,14 @@ class TestHttpHeaderGlobalsReachClient:
538
538
  monkeypatch.setattr(asana.TasksApi, "get_task", patched)
539
539
  result = make_runner().invoke(
540
540
  cmd,
541
- ["--default-header", "X-Foo=bar", "--default-header", "X-Baz=qux", "--task", "T"],
541
+ [
542
+ "--set-default-header",
543
+ "X-Foo=bar",
544
+ "--set-default-header",
545
+ "X-Baz=qux",
546
+ "--task",
547
+ "T",
548
+ ],
542
549
  )
543
550
  assert result.exit_code == 0, full_output(result)
544
551
  assert captured[0]["X-Foo"] == "bar"
@@ -547,7 +554,7 @@ class TestHttpHeaderGlobalsReachClient:
547
554
  def test_malformed_default_header_is_usage_error(self, monkeypatch: pytest.MonkeyPatch) -> None:
548
555
  cmd = _build_command("TasksApi", "get_task")
549
556
  _patch(monkeypatch, "TasksApi", "get_task", return_value={"data": {}})
550
- result = make_runner().invoke(cmd, ["--default-header", "noequals", "--task", "T"])
557
+ result = make_runner().invoke(cmd, ["--set-default-header", "noequals", "--task", "T"])
551
558
  assert result.exit_code == 2, full_output(result)
552
559
  assert "NAME=VALUE" in full_output(result)
553
560
 
@@ -53,8 +53,8 @@ EXPECTED_ALL_PARAMS: frozenset[str] = frozenset(
53
53
  }
54
54
  )
55
55
 
56
- # Public attributes on a default ``asana.Configuration()``. Not every settable
57
- # attribute is a CLI flag: object-/callable-typed members (``logger*``,
56
+ # Settable attributes the CLI knows about on a default ``asana.Configuration()``.
57
+ # Not every one is a CLI flag: object-/callable-typed members (``logger*``,
58
58
  # ``refresh_api_key_hook``) cannot be flags, and the inert auth fields
59
59
  # (``username`` / ``password`` / ``api_key`` / ``api_key_prefix``) are
60
60
  # deliberately NOT exposed — Asana auth is Bearer-token only, so they are
@@ -62,6 +62,14 @@ EXPECTED_ALL_PARAMS: frozenset[str] = frozenset(
62
62
  # set because the SDK's Configuration still declares them; this guard tracks the
63
63
  # SDK surface, not the CLI's. ``logger_format`` / ``logger_file`` reach the
64
64
  # ``logger*`` members via @property setters.
65
+ #
66
+ # This is the union across the supported asana range (>=5.0.2), pinned at the
67
+ # latest. ``test_no_unknown_configuration_settable_attrs`` checks only one
68
+ # direction — that the installed SDK exposes nothing OUTSIDE this set, since a
69
+ # *new* attribute is the actionable drift (it likely needs a flag). Older SDKs
70
+ # initialize fewer attributes (``access_token`` arrived in 5.0.6,
71
+ # ``retry_strategy`` in 5.1.0); those absences are tolerated so the guard passes
72
+ # on the floor as well as the pinned version.
65
73
  EXPECTED_CONFIGURATION_ATTRS: frozenset[str] = frozenset(
66
74
  {
67
75
  "access_token",
@@ -191,15 +199,15 @@ def test_all_methods_share_expected_all_params() -> None:
191
199
  )
192
200
 
193
201
 
194
- def test_configuration_settable_attrs_match() -> None:
202
+ def test_no_unknown_configuration_settable_attrs() -> None:
195
203
  got = frozenset(k for k in vars(asana.Configuration()) if not k.startswith("_"))
196
- assert got == EXPECTED_CONFIGURATION_ATTRS, (
197
- "asana.Configuration public attributes drifted:\n"
198
- f" added: {sorted(got - EXPECTED_CONFIGURATION_ATTRS)}\n"
199
- f" removed: {sorted(EXPECTED_CONFIGURATION_ATTRS - got)}\n"
204
+ unknown = got - EXPECTED_CONFIGURATION_ATTRS
205
+ assert not unknown, (
206
+ "asana.Configuration exposes settable attributes the CLI does not know "
207
+ f"about: {sorted(unknown)}\n"
200
208
  "A newly settable property likely needs a global flag + "
201
- "(Configuration: <name>) label in cli.py / click_ext.py. "
202
- "See docs/cli-sdk-mapping.md."
209
+ "(Configuration: <name>) label in cli.py / click_ext.py, then adding it "
210
+ "to EXPECTED_CONFIGURATION_ATTRS. See docs/cli-sdk-mapping.md."
203
211
  )
204
212
 
205
213
 
@@ -330,7 +330,7 @@ class TestAsanaSessionPaginationKwargs:
330
330
 
331
331
 
332
332
  # ---------------------------------------------------------------------------
333
- # ApiClient-instance settings (--user-agent / --default-header)
333
+ # ApiClient-instance settings (--user-agent / --set-default-header)
334
334
  # ---------------------------------------------------------------------------
335
335
 
336
336
 
@@ -224,13 +224,13 @@ class TestRetryFieldSchema:
224
224
  def _default_headers(*tokens: str) -> dict[str, str] | None:
225
225
  """Run ``default_header_callback`` with a real (throwaway) Click context,
226
226
  mirroring how ``multiple=True`` hands it a tuple of raw tokens."""
227
- param = click.Option(["--default-header"], multiple=True)
227
+ param = click.Option(["--set-default-header"], multiple=True)
228
228
  ctx = click.Context(click.Command("test"))
229
229
  return default_header_callback(ctx, param, tokens)
230
230
 
231
231
 
232
232
  class TestDefaultHeaderCallback:
233
- """``--default-header NAME=VALUE`` (repeatable) parser."""
233
+ """``--set-default-header NAME=VALUE`` (repeatable) parser."""
234
234
 
235
235
  def test_not_given_returns_none(self) -> None:
236
236
  # Matches the unset sentinel the other globals use.
File without changes
File without changes
File without changes