afterpython 0.1.0.dev7__py3-none-any.whl → 0.1.0.dev9__py3-none-any.whl

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 (44) hide show
  1. afterpython/_io/toml.py +5 -3
  2. afterpython/_paths.py +1 -1
  3. afterpython/_typing.py +1 -2
  4. afterpython/builders/__init__.py +0 -1
  5. afterpython/builders/metadata.py +1 -1
  6. afterpython/builders/molab.py +2 -1
  7. afterpython/cli/__init__.py +0 -1
  8. afterpython/cli/commands/add.py +67 -0
  9. afterpython/cli/commands/build.py +10 -9
  10. afterpython/cli/commands/clean.py +2 -1
  11. afterpython/cli/commands/commit.py +16 -1
  12. afterpython/cli/commands/dev.py +3 -2
  13. afterpython/cli/commands/init.py +26 -12
  14. afterpython/cli/commands/init_branch_rules.py +9 -0
  15. afterpython/cli/commands/install.py +8 -2
  16. afterpython/cli/commands/lock.py +23 -0
  17. afterpython/cli/commands/remove.py +67 -0
  18. afterpython/cli/commands/start.py +6 -3
  19. afterpython/cli/commands/sync.py +3 -3
  20. afterpython/cli/commands/update.py +26 -3
  21. afterpython/cli/main.py +75 -14
  22. afterpython/const.py +0 -1
  23. afterpython/pcu.py +3 -1
  24. afterpython/templates/ci-workflow-template.yml +197 -0
  25. afterpython/templates/dependabot-template.yml +30 -0
  26. afterpython/templates/deploy-workflow-template.yml +5 -5
  27. afterpython/templates/release-workflow-template.yml +3 -3
  28. afterpython/templates/ruff-template.toml +1 -1
  29. afterpython/tools/_afterpython.py +2 -1
  30. afterpython/tools/_git.py +28 -2
  31. afterpython/tools/branch_rules.py +106 -0
  32. afterpython/tools/commitizen.py +1 -2
  33. afterpython/tools/github_actions.py +23 -53
  34. afterpython/tools/myst.py +4 -2
  35. afterpython/tools/pre_commit.py +1 -3
  36. afterpython/tools/pyproject.py +4 -2
  37. afterpython/utils.py +9 -0
  38. {afterpython-0.1.0.dev7.dist-info → afterpython-0.1.0.dev9.dist-info}/METADATA +18 -8
  39. afterpython-0.1.0.dev9.dist-info/RECORD +52 -0
  40. {afterpython-0.1.0.dev7.dist-info → afterpython-0.1.0.dev9.dist-info}/WHEEL +1 -1
  41. afterpython/.pre-commit-config.yaml +0 -48
  42. afterpython/cz.toml +0 -27
  43. afterpython-0.1.0.dev7.dist-info/RECORD +0 -47
  44. {afterpython-0.1.0.dev7.dist-info → afterpython-0.1.0.dev9.dist-info}/entry_points.txt +0 -0
afterpython/_io/toml.py CHANGED
@@ -1,10 +1,12 @@
1
1
  from __future__ import annotations
2
+
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
5
- from tomlkit.toml_document import TOMLDocument
6
6
  from pathlib import Path
7
7
 
8
+ from tomlkit.toml_document import TOMLDocument
9
+
8
10
  import tomlkit
9
11
 
10
12
 
@@ -16,7 +18,7 @@ def _from_tomlkit(value):
16
18
  - tomlkit containers → plain Python containers
17
19
  - tomlkit primitives → plain Python primitives
18
20
  """
19
- from tomlkit.items import Array, InlineTable, Table, AoT
21
+ from tomlkit.items import AoT, Array, InlineTable, Table
20
22
 
21
23
  if value is None:
22
24
  return None
@@ -58,7 +60,7 @@ def _to_tomlkit(value):
58
60
  - List of dicts → array of inline tables: [{key = val}]
59
61
  - Nested dicts → nested tables with proper structure
60
62
  """
61
- from tomlkit import inline_table, array
63
+ from tomlkit import array, inline_table
62
64
 
63
65
  if value is None:
64
66
  return None
afterpython/_paths.py CHANGED
@@ -1,5 +1,5 @@
1
- from pathlib import Path
2
1
  from dataclasses import dataclass, field
2
+ from pathlib import Path
3
3
 
4
4
 
5
5
  @dataclass
afterpython/_typing.py CHANGED
@@ -1,5 +1,4 @@
1
- from typing import TypeAlias, Literal
2
-
1
+ from typing import Literal, TypeAlias
3
2
 
4
3
  NodeEnv: TypeAlias = dict[str, str]
5
4
  tContentType = Literal["doc", "blog", "tutorial", "example", "guide"]
@@ -1,7 +1,6 @@
1
1
  from afterpython.builders.metadata import build_metadata
2
2
  from afterpython.builders.molab import add_molab_badge_to_jupyter_notebooks
3
3
 
4
-
5
4
  __all__ = (
6
5
  "add_molab_badge_to_jupyter_notebooks",
7
6
  "build_metadata",
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
@@ -8,7 +9,6 @@ import json
8
9
 
9
10
  import afterpython as ap
10
11
 
11
-
12
12
  build_path = ap.paths.build_path
13
13
 
14
14
 
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
@@ -10,8 +11,8 @@ from pathlib import Path
10
11
  from pyproject_metadata import StandardMetadata
11
12
 
12
13
  import afterpython as ap
13
- from afterpython.tools.pyproject import read_metadata
14
14
  from afterpython.const import CONTENT_TYPES
15
+ from afterpython.tools.pyproject import read_metadata
15
16
 
16
17
 
17
18
  def _get_molab_badge() -> str:
@@ -1,4 +1,3 @@
1
1
  from afterpython.cli.main import afterpython_group
2
2
 
3
-
4
3
  __all__ = ["afterpython_group"]
@@ -0,0 +1,67 @@
1
+ import subprocess
2
+
3
+ import click
4
+ from click.exceptions import Exit
5
+
6
+ from afterpython.utils import has_pixi, has_uv
7
+
8
+
9
+ @click.command()
10
+ @click.option(
11
+ "--optional",
12
+ type=str,
13
+ default=None,
14
+ required=False,
15
+ help="Add to an optional dependency group (pixi: mapped to 'optional' feature)",
16
+ )
17
+ @click.option(
18
+ "--group",
19
+ type=str,
20
+ default=None,
21
+ required=False,
22
+ help="Add to a dependency group (pixi: mapped to same-named feature)",
23
+ )
24
+ @click.argument("lib", type=str, required=True)
25
+ def add(optional: str | None, group: str | None, lib: str):
26
+ """Add a new dependency to the project (manages both uv and pixi if present)"""
27
+ if not has_uv():
28
+ click.echo("uv not found. Please install uv first.")
29
+ return
30
+
31
+ if optional and group:
32
+ click.echo("Error: Cannot specify both --optional and --group")
33
+ raise Exit(1)
34
+
35
+ result = subprocess.run(
36
+ [
37
+ "uv",
38
+ "add",
39
+ *(["--optional", optional] if optional else []),
40
+ *(["--group", group] if group else []),
41
+ lib,
42
+ ],
43
+ check=False,
44
+ )
45
+ if result.returncode != 0:
46
+ raise Exit(result.returncode)
47
+
48
+ if has_pixi():
49
+ # NOTE: pixi doesn't support --optional, so we need to use --feature instead
50
+ if optional:
51
+ feature_name = "optional"
52
+ elif group:
53
+ feature_name = group
54
+ else:
55
+ feature_name = None
56
+ result = subprocess.run(
57
+ [
58
+ "pixi",
59
+ "add",
60
+ "--pypi",
61
+ *(["--feature", feature_name] if feature_name else []),
62
+ lib,
63
+ ],
64
+ check=False,
65
+ )
66
+ if result.returncode != 0:
67
+ raise Exit(result.returncode)
@@ -1,11 +1,13 @@
1
1
  from __future__ import annotations
2
- from typing import TYPE_CHECKING
2
+
3
3
  from collections.abc import Callable
4
+ from typing import TYPE_CHECKING
4
5
 
5
6
  if TYPE_CHECKING:
6
- from afterpython._typing import NodeEnv
7
7
  from pathlib import Path
8
8
 
9
+ from afterpython._typing import NodeEnv
10
+
9
11
  import os
10
12
  import shutil
11
13
  import subprocess
@@ -14,12 +16,12 @@ import click
14
16
  from click.exceptions import Exit
15
17
 
16
18
  import afterpython as ap
17
- from afterpython.utils import find_node_env
18
- from afterpython.const import CONTENT_TYPES
19
19
  from afterpython.builders import (
20
- build_metadata,
21
20
  add_molab_badge_to_jupyter_notebooks,
21
+ build_metadata,
22
22
  )
23
+ from afterpython.const import CONTENT_TYPES
24
+ from afterpython.utils import find_node_env
23
25
 
24
26
 
25
27
  def determine_base_path() -> str:
@@ -29,9 +31,9 @@ def determine_base_path() -> str:
29
31
  Empty string if custom domain is configured (no github.io in URL)
30
32
  /repo-name if using default GitHub Pages URL (contains github.io)
31
33
  """
34
+ from afterpython._io.toml import _from_tomlkit
32
35
  from afterpython.tools._afterpython import read_afterpython
33
36
  from afterpython.tools.pyproject import read_metadata
34
- from afterpython._io.toml import _from_tomlkit
35
37
 
36
38
  # Read website URL from afterpython.toml
37
39
  afterpython = read_afterpython()
@@ -77,7 +79,7 @@ def prebuild():
77
79
  _check_initialized()
78
80
  _clean_build_directory()
79
81
 
80
- if os.getenv("AP_MOLAB_BADGE", "0") == "1":
82
+ if os.getenv("AP_MOLAB_BADGE", "1") == "1":
81
83
  for content_type in CONTENT_TYPES:
82
84
  add_molab_badge_to_jupyter_notebooks(content_type)
83
85
 
@@ -167,8 +169,7 @@ def build(ctx, dev: bool, execute: bool):
167
169
 
168
170
  Use --execute to execute Jupyter notebooks for all content types.
169
171
  """
170
- from afterpython.utils import has_content_for_myst
171
- from afterpython.utils import handle_passthrough_help
172
+ from afterpython.utils import handle_passthrough_help, has_content_for_myst
172
173
 
173
174
  # Show both our options and myst's help and exit
174
175
  handle_passthrough_help(
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
@@ -10,8 +11,8 @@ import subprocess
10
11
  import click
11
12
  from click.exceptions import Exit
12
13
 
13
- from afterpython.utils import find_node_env, has_content_for_myst
14
14
  from afterpython.const import CONTENT_TYPES
15
+ from afterpython.utils import find_node_env, has_content_for_myst
15
16
 
16
17
 
17
18
  @click.command(
@@ -15,7 +15,13 @@ import click
15
15
  @click.option(
16
16
  "--no-cz", "--no-commitizen", is_flag=True, help="Skip running 'cz commit'"
17
17
  )
18
- def commit(ctx, no_cz: bool):
18
+ @click.option(
19
+ "--no-pc",
20
+ "--no-pre-commit",
21
+ is_flag=True,
22
+ help="Skip running pre-commit checks before commit",
23
+ )
24
+ def commit(ctx, no_cz: bool, no_pc: bool):
19
25
  """Run 'cz commit'"""
20
26
  from afterpython.utils import handle_passthrough_help
21
27
 
@@ -26,6 +32,15 @@ def commit(ctx, no_cz: bool):
26
32
  show_underlying=True,
27
33
  )
28
34
 
35
+ # Run pre-commit checks first (unless --no-pc is set)
36
+ if not no_pc:
37
+ result = subprocess.run(["ap", "pc", "run"])
38
+ if result.returncode != 0:
39
+ click.echo(
40
+ "❌ Pre-commit checks failed. Please fix the issues and try again."
41
+ )
42
+ ctx.exit(1)
43
+
29
44
  if not no_cz:
30
45
  subprocess.run(["ap", "cz", "commit", *ctx.args])
31
46
  else:
@@ -1,17 +1,18 @@
1
1
  from __future__ import annotations
2
+
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
5
6
  from afterpython._typing import NodeEnv
6
7
 
7
- import time
8
8
  import subprocess
9
+ import time
9
10
 
10
11
  import click
11
12
  from click.exceptions import Exit
12
13
 
13
- from afterpython.utils import find_node_env, find_available_port
14
14
  from afterpython.const import CONTENT_TYPES
15
+ from afterpython.utils import find_available_port, find_node_env
15
16
 
16
17
 
17
18
  @click.command(
@@ -22,7 +22,7 @@ def init_ruff_toml():
22
22
  "repos": [
23
23
  {
24
24
  "repo": "https://github.com/astral-sh/ruff-pre-commit",
25
- "rev": "v0.14.5",
25
+ "rev": "v0.14.6",
26
26
  "hooks": [
27
27
  {
28
28
  "id": "ruff-check",
@@ -47,18 +47,24 @@ def init_website():
47
47
 
48
48
 
49
49
  @click.command()
50
+ @click.option(
51
+ "--yes",
52
+ "-y",
53
+ is_flag=True,
54
+ help="Automatically answer yes to all prompts",
55
+ )
50
56
  @click.pass_context
51
- def init(ctx):
57
+ def init(ctx, yes):
52
58
  """Initialize afterpython with MyST Markdown (by default) and project website template"""
53
- from afterpython.tools.pyproject import init_pyproject
54
59
  from afterpython.tools._afterpython import init_afterpython
55
- from afterpython.tools.myst import init_myst
56
- from afterpython.tools.pre_commit import init_pre_commit
57
60
  from afterpython.tools.commitizen import init_commitizen
58
61
  from afterpython.tools.github_actions import (
59
- init_release_workflow,
60
- init_deploy_workflow,
62
+ create_dependabot,
63
+ create_workflow,
61
64
  )
65
+ from afterpython.tools.myst import init_myst
66
+ from afterpython.tools.pre_commit import init_pre_commit
67
+ from afterpython.tools.pyproject import init_pyproject
62
68
 
63
69
  paths = ctx.obj["paths"]
64
70
  click.echo("Initializing afterpython...")
@@ -79,20 +85,28 @@ def init(ctx):
79
85
 
80
86
  init_website()
81
87
 
82
- if click.confirm(
88
+ create_workflow("deploy")
89
+ create_workflow("ci")
90
+
91
+ if yes or click.confirm(
83
92
  f"\nCreate .pre-commit-config.yaml in {afterpython_path}?", default=True
84
93
  ):
85
94
  init_pre_commit()
86
95
 
87
- if click.confirm(f"\nCreate ruff.toml in {afterpython_path}?", default=True):
96
+ if yes or click.confirm(f"\nCreate ruff.toml in {afterpython_path}?", default=True):
88
97
  init_ruff_toml()
89
98
 
90
- if click.confirm(
99
+ if yes or click.confirm(
91
100
  f"\nCreate commitizen configuration (cz.toml) in {afterpython_path} "
92
101
  f"and release workflow in .github/workflows/release.yml?",
93
102
  default=True,
94
103
  ):
95
104
  init_commitizen()
96
- init_release_workflow()
105
+ create_workflow("release")
97
106
 
98
- init_deploy_workflow()
107
+ if yes or click.confirm(
108
+ "\nCreate Dependabot configuration (.github/dependabot.yml) "
109
+ "to auto-update GitHub Actions versions?",
110
+ default=True,
111
+ ):
112
+ create_dependabot()
@@ -0,0 +1,9 @@
1
+ import click
2
+
3
+
4
+ @click.command()
5
+ def init_branch_rules():
6
+ """Create default branch protection rules for the current repository"""
7
+ from afterpython.tools.branch_rules import create_default_branch_rules
8
+
9
+ create_default_branch_rules()
@@ -3,7 +3,7 @@ import subprocess
3
3
  import click
4
4
  from click.exceptions import Exit
5
5
 
6
- from afterpython.utils import has_uv
6
+ from afterpython.utils import has_pixi, has_uv
7
7
 
8
8
 
9
9
  @click.command(
@@ -13,10 +13,16 @@ from afterpython.utils import has_uv
13
13
  )
14
14
  )
15
15
  def install():
16
- """Run 'uv sync --all-extras --all-groups' to install all dependencies"""
16
+ """Install all dependencies (runs 'uv sync --all-extras --all-groups' and 'pixi install' if present)"""
17
17
  if not has_uv():
18
18
  click.echo("uv not found. Please install uv first.")
19
19
  return
20
+
20
21
  result = subprocess.run(["uv", "sync", "--all-extras", "--all-groups"], check=False)
21
22
  if result.returncode != 0:
22
23
  raise Exit(result.returncode)
24
+
25
+ if has_pixi():
26
+ result = subprocess.run(["pixi", "install"], check=False)
27
+ if result.returncode != 0:
28
+ raise Exit(result.returncode)
@@ -0,0 +1,23 @@
1
+ import subprocess
2
+
3
+ import click
4
+ from click.exceptions import Exit
5
+
6
+ from afterpython.utils import has_pixi, has_uv
7
+
8
+
9
+ @click.command()
10
+ def lock():
11
+ """Lock the dependencies (runs 'uv lock' and 'pixi lock' if present)"""
12
+ if not has_uv():
13
+ click.echo("uv not found. Please install uv first.")
14
+ return
15
+
16
+ result = subprocess.run(["uv", "lock"], check=False)
17
+ if result.returncode != 0:
18
+ raise Exit(result.returncode)
19
+
20
+ if has_pixi():
21
+ result = subprocess.run(["pixi", "lock"], check=False)
22
+ if result.returncode != 0:
23
+ raise Exit(result.returncode)
@@ -0,0 +1,67 @@
1
+ import subprocess
2
+
3
+ import click
4
+ from click.exceptions import Exit
5
+
6
+ from afterpython.utils import has_pixi, has_uv
7
+
8
+
9
+ @click.command()
10
+ @click.option(
11
+ "--optional",
12
+ type=str,
13
+ default=None,
14
+ required=False,
15
+ help="Remove from an optional dependency group (pixi: mapped to 'optional' feature)",
16
+ )
17
+ @click.option(
18
+ "--group",
19
+ type=str,
20
+ default=None,
21
+ required=False,
22
+ help="Remove from a dependency group (pixi: mapped to same-named feature)",
23
+ )
24
+ @click.argument("lib", type=str, required=True)
25
+ def remove(optional: str | None, group: str | None, lib: str):
26
+ """Remove a dependency from the project (manages both uv and pixi if present)"""
27
+ if not has_uv():
28
+ click.echo("uv not found. Please install uv first.")
29
+ return
30
+
31
+ if optional and group:
32
+ click.echo("Error: Cannot specify both --optional and --group")
33
+ raise Exit(1)
34
+
35
+ result = subprocess.run(
36
+ [
37
+ "uv",
38
+ "remove",
39
+ *(["--optional", optional] if optional else []),
40
+ *(["--group", group] if group else []),
41
+ lib,
42
+ ],
43
+ check=False,
44
+ )
45
+ if result.returncode != 0:
46
+ raise Exit(result.returncode)
47
+
48
+ if has_pixi():
49
+ # NOTE: pixi doesn't support --optional, so we need to use --feature instead
50
+ if optional:
51
+ feature_name = "optional"
52
+ elif group:
53
+ feature_name = group
54
+ else:
55
+ feature_name = None
56
+ result = subprocess.run(
57
+ [
58
+ "pixi",
59
+ "remove",
60
+ "--pypi",
61
+ *(["--feature", feature_name] if feature_name else []),
62
+ lib,
63
+ ],
64
+ check=False,
65
+ )
66
+ if result.returncode != 0:
67
+ raise Exit(result.returncode)
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
@@ -9,7 +10,6 @@ import subprocess
9
10
  import click
10
11
  from click.exceptions import Exit
11
12
 
12
-
13
13
  command_kwargs = {
14
14
  "add_help_option": False, # disable click's --help option so that myst start --help can work
15
15
  "context_settings": dict(
@@ -43,9 +43,12 @@ command_kwargs = {
43
43
  )
44
44
  def start(ctx, doc: bool, blog: bool, tutorial: bool, example: bool, guide: bool):
45
45
  """Simple wrapper for `myst start` for convenience"""
46
- from afterpython.utils import find_node_env, has_content_for_myst
47
46
  from afterpython.const import CONTENT_TYPES
48
- from afterpython.utils import handle_passthrough_help
47
+ from afterpython.utils import (
48
+ find_node_env,
49
+ handle_passthrough_help,
50
+ has_content_for_myst,
51
+ )
49
52
 
50
53
  # Show both our options and myst's help and exit
51
54
  handle_passthrough_help(
@@ -10,8 +10,8 @@ from afterpython.utils import convert_author_name_to_id
10
10
 
11
11
  def _sync_authors_yml(authors: list[tuple[str, str | None]]):
12
12
  """Sync authors.yml with authors in pyproject.toml"""
13
- from afterpython.tools.myst import update_authors_yml
14
13
  from afterpython._io.yaml import read_yaml
14
+ from afterpython.tools.myst import update_authors_yml
15
15
 
16
16
  # read myst.yml from docs path to get "version"
17
17
  doc_myst_yml = read_yaml(ap.paths.afterpython_path / "doc" / "myst.yml")
@@ -37,15 +37,15 @@ def _sync_authors_yml(authors: list[tuple[str, str | None]]):
37
37
  @click.command()
38
38
  def sync():
39
39
  """Sync between pyproject.toml+afterpython.toml and authors.yml+myst.yml files"""
40
+ from afterpython._io.toml import _from_tomlkit
40
41
  from afterpython.const import CONTENT_TYPES
41
42
  from afterpython.tools._afterpython import read_afterpython
43
+ from afterpython.tools.myst import update_myst_yml
42
44
  from afterpython.tools.pyproject import (
43
45
  read_metadata,
44
46
  read_pyproject,
45
47
  write_pyproject,
46
48
  )
47
- from afterpython._io.toml import _from_tomlkit
48
- from afterpython.tools.myst import update_myst_yml
49
49
 
50
50
  pyproject: StandardMetadata = read_metadata()
51
51
  project_name = str(pyproject.name)
@@ -1,12 +1,13 @@
1
1
  from __future__ import annotations
2
+
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
5
6
  from afterpython._typing import NodeEnv
6
7
  from afterpython.pcu import Dependencies
7
8
 
8
- import subprocess
9
9
  import shutil
10
+ import subprocess
10
11
 
11
12
  import click
12
13
  from click.exceptions import Exit
@@ -33,7 +34,7 @@ def update():
33
34
  def dependencies(upgrade: bool, all: bool):
34
35
  """Update pyproject.toml dependencies to the latest version"""
35
36
  from afterpython.pcu import get_dependencies, update_dependencies
36
- from afterpython.utils import has_uv
37
+ from afterpython.utils import has_pixi, has_uv
37
38
 
38
39
  dependencies: Dependencies = get_dependencies()
39
40
  has_at_least_one_update = False
@@ -79,7 +80,9 @@ def dependencies(upgrade: bool, all: bool):
79
80
  raise Exit(result.returncode)
80
81
  click.echo(
81
82
  click.style(
82
- "✓ All dependencies upgraded successfully 🎉", fg="green", bold=True
83
+ "✓ All dependencies in pyproject.toml upgraded successfully 🎉",
84
+ fg="green",
85
+ bold=True,
83
86
  )
84
87
  )
85
88
  else:
@@ -89,6 +92,26 @@ def dependencies(upgrade: bool, all: bool):
89
92
  if all:
90
93
  subprocess.run(["ap", "pre-commit", "autoupdate"])
91
94
  click.echo("All pre-commit hooks updated successfully.")
95
+ if has_pixi():
96
+ click.echo("Upgrading dependencies with pixi...")
97
+ result = subprocess.run(
98
+ ["pixi", "upgrade", "--exclude", "python"], check=False
99
+ )
100
+ if result.returncode != 0:
101
+ raise Exit(result.returncode)
102
+ result = subprocess.run(["pixi", "lock"], check=False)
103
+ if result.returncode != 0:
104
+ raise Exit(result.returncode)
105
+ result = subprocess.run(["pixi", "install"], check=False)
106
+ if result.returncode != 0:
107
+ raise Exit(result.returncode)
108
+ click.echo(
109
+ click.style(
110
+ "✓ All dependencies in pixi.toml upgraded successfully 🎉",
111
+ fg="green",
112
+ bold=True,
113
+ )
114
+ )
92
115
 
93
116
 
94
117
  update.add_command(dependencies, name="deps") # alias for "dependencies"