uv-development-toggle 0.2.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.
- uv_development_toggle-0.2.1/.copier/update.sh +5 -0
- uv_development_toggle-0.2.1/.copier-answers.yml +2 -0
- uv_development_toggle-0.2.1/.envrc +14 -0
- uv_development_toggle-0.2.1/.github/dependabot.yml +12 -0
- uv_development_toggle-0.2.1/.github/workflows/build_and_publish_text_matrix.yml +65 -0
- uv_development_toggle-0.2.1/.github/workflows/repo-sync.yml +16 -0
- uv_development_toggle-0.2.1/.gitignore +129 -0
- uv_development_toggle-0.2.1/.tool-versions +2 -0
- uv_development_toggle-0.2.1/.vscode/settings.json +36 -0
- uv_development_toggle-0.2.1/CHANGELOG.md +30 -0
- uv_development_toggle-0.2.1/Makefile +7 -0
- uv_development_toggle-0.2.1/PKG-INFO +56 -0
- uv_development_toggle-0.2.1/README.md +45 -0
- uv_development_toggle-0.2.1/copier.yml +68 -0
- uv_development_toggle-0.2.1/docker-compose.yml +36 -0
- uv_development_toggle-0.2.1/pyproject.toml +25 -0
- uv_development_toggle-0.2.1/script.py +236 -0
- uv_development_toggle-0.2.1/tests/__init__.py +0 -0
- uv_development_toggle-0.2.1/tests/test_import.py +8 -0
- uv_development_toggle-0.2.1/tests/test_import.py.jinja +8 -0
- uv_development_toggle-0.2.1/uv.lock +132 -0
- uv_development_toggle-0.2.1/uv_development_toggle/__init__.py +243 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
# this exact file name is required in this exact location if updates are to work
|
|
2
|
+
{"_commit": "v0.2.1-27-gb520f68", "_src_path": "https://github.com/iloveitaly/python-package-template", "project_name": "uv-development-toggle", "full_name": "Michael Bianco", "email": "mike@mikebian.co", "github_username": "iloveitaly"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
layout uv
|
|
2
|
+
|
|
3
|
+
# if you are using orb for local development, this will work just fine
|
|
4
|
+
export DATABASE_HOST=${DATABASE_HOST:-postgres.$(basename $PWD).python-starter-template.orb.local}
|
|
5
|
+
export REDIS_HOST=${REDIS_HOST:-redis.$(basename $PWD).python-starter-template.orb.local}
|
|
6
|
+
|
|
7
|
+
export POSTGRES_USER=root
|
|
8
|
+
export POSTGRES_PASSWORD=password
|
|
9
|
+
export POSTGRES_DB=development
|
|
10
|
+
|
|
11
|
+
export DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${DATABASE_HOST}:5432/development
|
|
12
|
+
export REDIS_URL=redis://${REDIS_HOST}:6379/1
|
|
13
|
+
|
|
14
|
+
export LOG_LEVEL=DEBUG
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: Build and Publish to PyPI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- main
|
|
6
|
+
- master
|
|
7
|
+
|
|
8
|
+
# by default, permissions are read-only, read + write is required for git pushes
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
env:
|
|
13
|
+
PIP_DEFAULT_TIMEOUT: 60
|
|
14
|
+
PIP_RETRIES: 5
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
# if you want to test across multiple python versions
|
|
18
|
+
matrix-test:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
matrix:
|
|
22
|
+
python-version: ["3.13", "3.12", "3.11", "3.10", "3.9"]
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- uses: jdx/mise-action@v2
|
|
26
|
+
- run: mise use python@${{ matrix.python-version }}
|
|
27
|
+
- run: uv sync
|
|
28
|
+
- name: Make sure the CLI runs properly
|
|
29
|
+
run: uv run ${{ github.event.repository.name }} --help
|
|
30
|
+
- run: uv run pytest
|
|
31
|
+
|
|
32
|
+
build-and-publish:
|
|
33
|
+
needs: matrix-test
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: jdx/mise-action@v2
|
|
38
|
+
- run: uv sync
|
|
39
|
+
- name: Conventional Changelog Action
|
|
40
|
+
id: changelog
|
|
41
|
+
uses: TriPSs/conventional-changelog-action@v6
|
|
42
|
+
with:
|
|
43
|
+
github-token: ${{ secrets.github_token }}
|
|
44
|
+
version-file: "./pyproject.toml"
|
|
45
|
+
version-path: "project.version"
|
|
46
|
+
fallback-version: "0.1.0"
|
|
47
|
+
output-file: "CHANGELOG.md"
|
|
48
|
+
|
|
49
|
+
# NOTE must run after versioning otherwise the right version won't be pushed
|
|
50
|
+
- name: Build distribution package
|
|
51
|
+
if: ${{ steps.changelog.outputs.skipped == 'false' }}
|
|
52
|
+
run: uv build
|
|
53
|
+
|
|
54
|
+
- name: Publish to PyPI
|
|
55
|
+
if: ${{ steps.changelog.outputs.skipped == 'false' }}
|
|
56
|
+
# `gh secret set PYPI_API_TOKEN --app actions --body $PYPI_API_TOKEN`
|
|
57
|
+
run: uv publish --token ${{ secrets.PYPI_API_TOKEN }}
|
|
58
|
+
|
|
59
|
+
- name: Github Release
|
|
60
|
+
if: ${{ steps.changelog.outputs.skipped == 'false' }}
|
|
61
|
+
uses: softprops/action-gh-release@v2
|
|
62
|
+
with:
|
|
63
|
+
# output options: https://github.com/TriPSs/conventional-changelog-action#outputs
|
|
64
|
+
body: ${{ steps.changelog.outputs.clean_changelog }}
|
|
65
|
+
tag_name: ${{ steps.changelog.outputs.tag }}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: Repository Metadata Sync
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
repo_sync:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- name: Fetching Local Repository
|
|
12
|
+
uses: actions/checkout@v4
|
|
13
|
+
- name: Repository Metadata Sync
|
|
14
|
+
uses: iloveitaly/github-actions-metadata-sync@main
|
|
15
|
+
with:
|
|
16
|
+
TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
# Usually these files are written by a python script from a template
|
|
32
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
33
|
+
*.manifest
|
|
34
|
+
*.spec
|
|
35
|
+
|
|
36
|
+
# Installer logs
|
|
37
|
+
pip-log.txt
|
|
38
|
+
pip-delete-this-directory.txt
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
*.py,cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
target/
|
|
76
|
+
|
|
77
|
+
# Jupyter Notebook
|
|
78
|
+
.ipynb_checkpoints
|
|
79
|
+
|
|
80
|
+
# IPython
|
|
81
|
+
profile_default/
|
|
82
|
+
ipython_config.py
|
|
83
|
+
|
|
84
|
+
# pyenv
|
|
85
|
+
.python-version
|
|
86
|
+
|
|
87
|
+
# pipenv
|
|
88
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
89
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
90
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
91
|
+
# install all needed dependencies.
|
|
92
|
+
#Pipfile.lock
|
|
93
|
+
|
|
94
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
95
|
+
__pypackages__/
|
|
96
|
+
|
|
97
|
+
# Celery stuff
|
|
98
|
+
celerybeat-schedule
|
|
99
|
+
celerybeat.pid
|
|
100
|
+
|
|
101
|
+
# SageMath parsed files
|
|
102
|
+
*.sage.py
|
|
103
|
+
|
|
104
|
+
# Environments
|
|
105
|
+
.env
|
|
106
|
+
.venv
|
|
107
|
+
env/
|
|
108
|
+
venv/
|
|
109
|
+
ENV/
|
|
110
|
+
env.bak/
|
|
111
|
+
venv.bak/
|
|
112
|
+
|
|
113
|
+
# Spyder project settings
|
|
114
|
+
.spyderproject
|
|
115
|
+
.spyproject
|
|
116
|
+
|
|
117
|
+
# Rope project settings
|
|
118
|
+
.ropeproject
|
|
119
|
+
|
|
120
|
+
# mkdocs documentation
|
|
121
|
+
/site
|
|
122
|
+
|
|
123
|
+
# mypy
|
|
124
|
+
.mypy_cache/
|
|
125
|
+
.dmypy.json
|
|
126
|
+
dmypy.json
|
|
127
|
+
|
|
128
|
+
# Pyre type checker
|
|
129
|
+
.pyre/
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"[python]": {
|
|
3
|
+
"editor.formatOnSave": true,
|
|
4
|
+
"editor.defaultFormatter": "charliermarsh.ruff",
|
|
5
|
+
"editor.codeActionsOnSave": {
|
|
6
|
+
"source.fixAll": "explicit",
|
|
7
|
+
"source.organizeImports": "explicit"
|
|
8
|
+
},
|
|
9
|
+
"editor.tabSize": 4
|
|
10
|
+
},
|
|
11
|
+
"[toml]": {
|
|
12
|
+
"editor.formatOnSave": true,
|
|
13
|
+
"editor.tabSize": 4
|
|
14
|
+
},
|
|
15
|
+
"python.analysis.autoFormatStrings": true,
|
|
16
|
+
|
|
17
|
+
// for import autosuggest
|
|
18
|
+
"python.analysis.indexing": true,
|
|
19
|
+
"python.analysis.autoImportCompletions": true,
|
|
20
|
+
|
|
21
|
+
"python.analysis.packageIndexDepths": [
|
|
22
|
+
{
|
|
23
|
+
"name": "",
|
|
24
|
+
"depth": 3,
|
|
25
|
+
"includeAllSymbols": true
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
|
|
29
|
+
"cSpell.words": ["openai", "httpx"],
|
|
30
|
+
|
|
31
|
+
"files.exclude": {
|
|
32
|
+
".ruff_cache": true,
|
|
33
|
+
".pytest_cache": true,
|
|
34
|
+
".venv": true
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
## [0.2.1](https://github.com/iloveitaly/uv-development-toggle/compare/v0.2.0...v0.2.1) (2025-03-19)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* clarify argparse help messages for module toggling ([fe9200b](https://github.com/iloveitaly/uv-development-toggle/commit/fe9200b4c886e24b028ec61444c79bd0e5eb7faf))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# [0.2.0](https://github.com/iloveitaly/uv-development-toggle/compare/d6ebea655086f9d0d430a1f01062222e96796a55...v0.2.0) (2025-03-19)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* adjust module path resolution with env variable ([d6ebea6](https://github.com/iloveitaly/uv-development-toggle/commit/d6ebea655086f9d0d430a1f01062222e96796a55))
|
|
16
|
+
* handle missing GitHub URL more robustly in script.py ([f9a4111](https://github.com/iloveitaly/uv-development-toggle/commit/f9a4111a207a094153d8a8525377a674b9ceb34b))
|
|
17
|
+
* handle missing project_urls in get_pypi_homepage function ([2dc7a62](https://github.com/iloveitaly/uv-development-toggle/commit/2dc7a624a188f38b3d63680fdfa739303643addf))
|
|
18
|
+
* handle missing pyproject.toml in toggle_module_source ([9c55ee8](https://github.com/iloveitaly/uv-development-toggle/commit/9c55ee8895db5dfa426e6800a81ce627c42f9179))
|
|
19
|
+
* handle module naming variations in toggle_module_source function ([db36fa2](https://github.com/iloveitaly/uv-development-toggle/commit/db36fa26b6d2b6f0cbd87b3b8aa3da081becbf2d))
|
|
20
|
+
* prioritize GitHub links in get_pypi_homepage function ([91fc748](https://github.com/iloveitaly/uv-development-toggle/commit/91fc748219166f8c15c48624dab00d70c7198e6a))
|
|
21
|
+
* specify VCS reference in copier update script ([a206262](https://github.com/iloveitaly/uv-development-toggle/commit/a2062628e189fc67569fe3f479401222d8d6769d))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
* add PyPI force option in toggle_module_source function ([9fbd995](https://github.com/iloveitaly/uv-development-toggle/commit/9fbd9958b68a5d37dd042259eff797d566920a8e))
|
|
27
|
+
* conditional git init and add import tests ([8757f65](https://github.com/iloveitaly/uv-development-toggle/commit/8757f65c38e717b9389f50fa14e65f556b91504e))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: uv-development-toggle
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Easily toggle between development and production packages with uv. Quickly close development packages locally.
|
|
5
|
+
Project-URL: Repository, https://github.com/iloveitaly/uv-development-toggle
|
|
6
|
+
Author-email: Michael Bianco <mike@mikebian.co>
|
|
7
|
+
Keywords: development,package,uv
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Requires-Dist: tomlkit>=0.13.2
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Python Development Source Toggler
|
|
14
|
+
|
|
15
|
+
A utility script for easily switching between local development and published sources for Python packages in your `pyproject.toml`.
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- Automatically toggles between local development paths and GitHub sources
|
|
20
|
+
- Preserves TOML file comments and structure
|
|
21
|
+
- Automatically clones repositories when switching to local development
|
|
22
|
+
- Supports branch tracking
|
|
23
|
+
- Falls back to PyPI metadata if direct GitHub repository is not found
|
|
24
|
+
- Integrates with GitHub CLI for username detection
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
pip install -r requirements.txt
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
To toggle a module named "activemodel":
|
|
35
|
+
|
|
36
|
+
```shell
|
|
37
|
+
pip install uv-development-toggle
|
|
38
|
+
uv-development-toggle activemodel --published
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This will:
|
|
42
|
+
|
|
43
|
+
1. Check if the package exists in your `PYTHON_DEVELOPMENT_TOGGLE` directory
|
|
44
|
+
2. If switching to local and the repository doesn't exist, clone it automatically (attempts to determine the repo URL from pypi information)
|
|
45
|
+
3. Update your `pyproject.toml` with the appropriate source configuration
|
|
46
|
+
4. Preserve any existing branch information when toggling
|
|
47
|
+
|
|
48
|
+
### Arguments
|
|
49
|
+
|
|
50
|
+
- `MODULE_NAME`: The name of the Python module to toggle
|
|
51
|
+
- `--local`: Force using local development path
|
|
52
|
+
- `--published`: Force using published source
|
|
53
|
+
|
|
54
|
+
### Environment Variables
|
|
55
|
+
|
|
56
|
+
- `PYTHON_DEVELOPMENT_TOGGLE`: Directory for local development repositories (default: "pypi")
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
|
|
2
|
+
# Python Development Source Toggler
|
|
3
|
+
|
|
4
|
+
A utility script for easily switching between local development and published sources for Python packages in your `pyproject.toml`.
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
|
|
8
|
+
- Automatically toggles between local development paths and GitHub sources
|
|
9
|
+
- Preserves TOML file comments and structure
|
|
10
|
+
- Automatically clones repositories when switching to local development
|
|
11
|
+
- Supports branch tracking
|
|
12
|
+
- Falls back to PyPI metadata if direct GitHub repository is not found
|
|
13
|
+
- Integrates with GitHub CLI for username detection
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
pip install -r requirements.txt
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
To toggle a module named "activemodel":
|
|
24
|
+
|
|
25
|
+
```shell
|
|
26
|
+
pip install uv-development-toggle
|
|
27
|
+
uv-development-toggle activemodel --published
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This will:
|
|
31
|
+
|
|
32
|
+
1. Check if the package exists in your `PYTHON_DEVELOPMENT_TOGGLE` directory
|
|
33
|
+
2. If switching to local and the repository doesn't exist, clone it automatically (attempts to determine the repo URL from pypi information)
|
|
34
|
+
3. Update your `pyproject.toml` with the appropriate source configuration
|
|
35
|
+
4. Preserve any existing branch information when toggling
|
|
36
|
+
|
|
37
|
+
### Arguments
|
|
38
|
+
|
|
39
|
+
- `MODULE_NAME`: The name of the Python module to toggle
|
|
40
|
+
- `--local`: Force using local development path
|
|
41
|
+
- `--published`: Force using published source
|
|
42
|
+
|
|
43
|
+
### Environment Variables
|
|
44
|
+
|
|
45
|
+
- `PYTHON_DEVELOPMENT_TOGGLE`: Directory for local development repositories (default: "pypi")
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# NOTE some limitations of copier:
|
|
2
|
+
#
|
|
3
|
+
# - any extensions must be installed manually
|
|
4
|
+
# - you cannot use dst_path as default answers
|
|
5
|
+
|
|
6
|
+
_min_copier_version: 9.4.1
|
|
7
|
+
|
|
8
|
+
_answers_file: .copier/.copier-answers.yml
|
|
9
|
+
|
|
10
|
+
_jinja_extensions:
|
|
11
|
+
- jinja2_shell_extension.ShellExtension
|
|
12
|
+
|
|
13
|
+
_message_after_copy: |
|
|
14
|
+
Next steps:
|
|
15
|
+
|
|
16
|
+
1. Customize secrets in .envrc
|
|
17
|
+
2. Run `direnv allow`
|
|
18
|
+
3. Set PYPI_API_TOKEN on GH actions
|
|
19
|
+
4. Set GH_TOKEN on GH actions
|
|
20
|
+
|
|
21
|
+
project_name:
|
|
22
|
+
type: str
|
|
23
|
+
help: Dash separated project slug
|
|
24
|
+
default: "{{ \"basename $(pwd)\" | shell() | trim | regex_replace(' ', '-') | regex_replace('_', '-') }}"
|
|
25
|
+
validator: >-
|
|
26
|
+
{% if not (project_name | regex_search('^[a-z][a-z0-9-_]+$')) %}
|
|
27
|
+
project_name must start with a letter, followed one or more letters, digits or dashes all lowercase.
|
|
28
|
+
{% endif %}
|
|
29
|
+
|
|
30
|
+
# https://github.com/superlinear-ai/substrate/blob/main/copier.yml
|
|
31
|
+
project_name_snake_case:
|
|
32
|
+
when: false
|
|
33
|
+
default: "{{ project_name | lower | replace('-', '_') }}"
|
|
34
|
+
|
|
35
|
+
full_name:
|
|
36
|
+
type: str
|
|
37
|
+
help: Full name of the project owner (for pypi)
|
|
38
|
+
default: "{{ \"git config --global user.name\" | shell() | trim }}"
|
|
39
|
+
|
|
40
|
+
email:
|
|
41
|
+
type: str
|
|
42
|
+
help: Email of the project owner (for pypi)
|
|
43
|
+
default: "{{ \"git config --global user.email\" | shell() | trim }}"
|
|
44
|
+
|
|
45
|
+
github_username:
|
|
46
|
+
type: str
|
|
47
|
+
help: GitHub username of the project owner (for pypi)
|
|
48
|
+
default: "{{ \"gh api user --jq '.login'\" | shell() | trim }}"
|
|
49
|
+
|
|
50
|
+
_exclude:
|
|
51
|
+
- TODO
|
|
52
|
+
- /.git
|
|
53
|
+
- /README.md
|
|
54
|
+
- /CHANGELOG.md
|
|
55
|
+
- /LICENSE
|
|
56
|
+
- /uv.lock
|
|
57
|
+
- /metadata.json
|
|
58
|
+
|
|
59
|
+
_tasks:
|
|
60
|
+
- '[ ! -d .git ] && git init'
|
|
61
|
+
- touch README.md
|
|
62
|
+
- uv sync
|
|
63
|
+
- git add -A
|
|
64
|
+
- git commit -m "🎉 Initial commit"
|
|
65
|
+
# - ["{{ _copier_python }}", .copier/bootstrap.py]
|
|
66
|
+
|
|
67
|
+
# although it's annoying to have the .copier-answers.yml file in the root, it allows `copier update`
|
|
68
|
+
# to work properly
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# ports are not exposed locally, instead dynamic orb domains are used to avoid clobbering localhost ports
|
|
2
|
+
services:
|
|
3
|
+
# port 5432
|
|
4
|
+
postgres:
|
|
5
|
+
image: postgres:latest
|
|
6
|
+
environment:
|
|
7
|
+
POSTGRES_USER: root
|
|
8
|
+
POSTGRES_PASSWORD: password
|
|
9
|
+
POSTGRES_DB: development
|
|
10
|
+
ports:
|
|
11
|
+
- ${CI:+5432}:5432
|
|
12
|
+
volumes:
|
|
13
|
+
- app_postgres_data:/var/lib/postgresql/data/
|
|
14
|
+
healthcheck:
|
|
15
|
+
# if you customize the default user at all, this healthcheck with clutter your logs
|
|
16
|
+
# since the default username + database does not match up
|
|
17
|
+
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
|
18
|
+
interval: 2s
|
|
19
|
+
timeout: 5s
|
|
20
|
+
retries: 3
|
|
21
|
+
|
|
22
|
+
# port 6379
|
|
23
|
+
redis:
|
|
24
|
+
image: redis:latest
|
|
25
|
+
restart: always
|
|
26
|
+
ports:
|
|
27
|
+
- ${CI:+6379}:6379
|
|
28
|
+
healthcheck:
|
|
29
|
+
test: ["CMD-SHELL", "redis-cli ping"]
|
|
30
|
+
interval: 2s
|
|
31
|
+
timeout: 5s
|
|
32
|
+
retries: 3
|
|
33
|
+
|
|
34
|
+
volumes:
|
|
35
|
+
# NOTE redis data is not persisted, assumed to be ephemeral
|
|
36
|
+
app_postgres_data:
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "uv-development-toggle"
|
|
3
|
+
version = "0.2.1"
|
|
4
|
+
description = "Easily toggle between development and production packages with uv. Quickly close development packages locally."
|
|
5
|
+
keywords = ["uv", "development", "package"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.9"
|
|
8
|
+
dependencies = ["tomlkit>=0.13.2"]
|
|
9
|
+
authors = [{ name = "Michael Bianco", email = "mike@mikebian.co" }]
|
|
10
|
+
urls = { "Repository" = "https://github.com/iloveitaly/uv-development-toggle" }
|
|
11
|
+
|
|
12
|
+
# additional packaging information: https://packaging.python.org/en/latest/specifications/core-metadata/#license
|
|
13
|
+
[project.scripts]
|
|
14
|
+
uv-development-toggle = "uv_development_toggle:main"
|
|
15
|
+
|
|
16
|
+
# https://github.com/astral-sh/uv/issues/5200
|
|
17
|
+
[tool.uv]
|
|
18
|
+
package = true
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["hatchling"]
|
|
22
|
+
build-backend = "hatchling.build"
|
|
23
|
+
|
|
24
|
+
[dependency-groups]
|
|
25
|
+
dev = ["pytest>=8.3.3"]
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from urllib.request import urlopen
|
|
9
|
+
|
|
10
|
+
import tomlkit
|
|
11
|
+
|
|
12
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_github_username() -> str:
|
|
17
|
+
logger.debug("Attempting to get GitHub username")
|
|
18
|
+
# Try gh cli first
|
|
19
|
+
try:
|
|
20
|
+
result = subprocess.run(["gh", "api", "user"], capture_output=True, text=True)
|
|
21
|
+
if result.returncode == 0:
|
|
22
|
+
username = json.loads(result.stdout)["login"]
|
|
23
|
+
logger.debug(f"Found username via gh cli: {username}")
|
|
24
|
+
return username
|
|
25
|
+
except FileNotFoundError:
|
|
26
|
+
logger.debug("gh cli not found, trying git config")
|
|
27
|
+
|
|
28
|
+
# Fall back to git config
|
|
29
|
+
try:
|
|
30
|
+
result = subprocess.run(
|
|
31
|
+
["git", "config", "user.name"], capture_output=True, text=True
|
|
32
|
+
)
|
|
33
|
+
if result.returncode == 0:
|
|
34
|
+
username = result.stdout.strip()
|
|
35
|
+
logger.debug(f"Found username via git config: {username}")
|
|
36
|
+
return username
|
|
37
|
+
except FileNotFoundError:
|
|
38
|
+
logger.debug("git not found")
|
|
39
|
+
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def check_github_repo_exists(username: str, repo: str) -> bool:
|
|
44
|
+
logger.debug(f"Checking if repo exists: {username}/{repo}")
|
|
45
|
+
try:
|
|
46
|
+
urlopen(f"https://github.com/{username}/{repo}")
|
|
47
|
+
logger.debug("Repository found")
|
|
48
|
+
return True
|
|
49
|
+
except:
|
|
50
|
+
logger.debug("Repository not found")
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_pypi_info(package_name: str) -> dict:
|
|
55
|
+
logger.debug(f"Fetching PyPI data for {package_name}")
|
|
56
|
+
try:
|
|
57
|
+
with urlopen(f"https://pypi.org/pypi/{package_name}/json") as response:
|
|
58
|
+
data = json.loads(response.read())
|
|
59
|
+
logger.debug("Successfully fetched PyPI data")
|
|
60
|
+
return data
|
|
61
|
+
except:
|
|
62
|
+
logger.debug("Failed to fetch PyPI data")
|
|
63
|
+
return {}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_pypi_homepage(package_name: str) -> str:
|
|
67
|
+
data = get_pypi_info(package_name)
|
|
68
|
+
homepage = data.get("info", {}).get("home_page", "")
|
|
69
|
+
|
|
70
|
+
# Ensure homepage is not None
|
|
71
|
+
homepage = homepage or ""
|
|
72
|
+
|
|
73
|
+
# Check if homepage contains github.com
|
|
74
|
+
if homepage and "github.com" in homepage:
|
|
75
|
+
return homepage
|
|
76
|
+
|
|
77
|
+
# Check all project URLs for GitHub links
|
|
78
|
+
project_urls = data.get("info", {}).get("project_urls") or {}
|
|
79
|
+
|
|
80
|
+
# First try the repository link as priority
|
|
81
|
+
if (
|
|
82
|
+
"repository" in project_urls
|
|
83
|
+
and project_urls["repository"]
|
|
84
|
+
and "github.com" in project_urls["repository"]
|
|
85
|
+
):
|
|
86
|
+
return project_urls["repository"]
|
|
87
|
+
|
|
88
|
+
# Then look in all other project links
|
|
89
|
+
for url_name, url in project_urls.items():
|
|
90
|
+
if url and "github.com" in url:
|
|
91
|
+
return url
|
|
92
|
+
|
|
93
|
+
# Return homepage even if it's not a GitHub URL, or empty string
|
|
94
|
+
return homepage
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def clone_repo(github_url: str, target_path: Path):
|
|
98
|
+
logger.info(f"Cloning {github_url} into {target_path}")
|
|
99
|
+
subprocess.run(["git", "clone", github_url, str(target_path)], check=True)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def get_current_branch(repo_path: Path) -> str:
|
|
103
|
+
result = subprocess.run(
|
|
104
|
+
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
105
|
+
capture_output=True,
|
|
106
|
+
text=True,
|
|
107
|
+
cwd=str(repo_path),
|
|
108
|
+
)
|
|
109
|
+
return result.stdout.strip()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def toggle_module_source(
|
|
113
|
+
module_name: str,
|
|
114
|
+
force_local: bool = False,
|
|
115
|
+
force_published: bool = False,
|
|
116
|
+
force_pypi: bool = False,
|
|
117
|
+
):
|
|
118
|
+
pyproject_path = Path("pyproject.toml")
|
|
119
|
+
|
|
120
|
+
# Check if the pyproject.toml exists
|
|
121
|
+
if not pyproject_path.exists():
|
|
122
|
+
logger.error("No pyproject.toml found, are you in the right folder?")
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
|
|
125
|
+
# Read with tomlkit to preserve comments and structure
|
|
126
|
+
with open(pyproject_path) as f:
|
|
127
|
+
config = tomlkit.load(f)
|
|
128
|
+
|
|
129
|
+
sources = config["tool"]["uv"]["sources"]
|
|
130
|
+
current_source = sources.get(module_name, {})
|
|
131
|
+
|
|
132
|
+
# Handle PyPI option
|
|
133
|
+
if force_pypi:
|
|
134
|
+
# For PyPI, we remove the source entry or set it to {} to use default PyPI source
|
|
135
|
+
if module_name in sources:
|
|
136
|
+
logger.info(f"Removing custom source for {module_name} to use PyPI version")
|
|
137
|
+
del sources[module_name]
|
|
138
|
+
else:
|
|
139
|
+
logger.info(f"Already using PyPI version for {module_name}")
|
|
140
|
+
|
|
141
|
+
# Write back with preserved comments
|
|
142
|
+
with open(pyproject_path, "w") as f:
|
|
143
|
+
tomlkit.dump(config, f)
|
|
144
|
+
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
dev_toggle_dir = os.environ.get("PYTHON_DEVELOPMENT_TOGGLE", "pypi")
|
|
148
|
+
local_path_default = Path(f"{dev_toggle_dir}/{module_name}")
|
|
149
|
+
local_path_dash = Path(f"{dev_toggle_dir}/{module_name.replace('_', '-')}")
|
|
150
|
+
local_path_underscore = Path(f"{dev_toggle_dir}/{module_name.replace('-', '_')}")
|
|
151
|
+
|
|
152
|
+
if local_path_default.exists():
|
|
153
|
+
local_path = local_path_default
|
|
154
|
+
elif local_path_dash.exists():
|
|
155
|
+
local_path = local_path_dash
|
|
156
|
+
elif local_path_underscore.exists():
|
|
157
|
+
local_path = local_path_underscore
|
|
158
|
+
else:
|
|
159
|
+
local_path = local_path_default
|
|
160
|
+
|
|
161
|
+
# Get current branch if local repo exists
|
|
162
|
+
current_branch = None
|
|
163
|
+
if local_path.exists():
|
|
164
|
+
current_branch = get_current_branch(local_path)
|
|
165
|
+
if current_branch in ("master", "main"):
|
|
166
|
+
current_branch = None
|
|
167
|
+
|
|
168
|
+
# Try to find the correct GitHub source
|
|
169
|
+
github_url = None
|
|
170
|
+
username = get_github_username()
|
|
171
|
+
|
|
172
|
+
if username and check_github_repo_exists(username, module_name):
|
|
173
|
+
github_url = f"https://github.com/{username}/{module_name}.git"
|
|
174
|
+
else:
|
|
175
|
+
pypi_homepage = get_pypi_homepage(module_name)
|
|
176
|
+
if "github.com" in pypi_homepage:
|
|
177
|
+
github_url = f"{pypi_homepage}.git"
|
|
178
|
+
|
|
179
|
+
if not github_url:
|
|
180
|
+
logger.warning(f"Could not determine GitHub URL for {module_name}")
|
|
181
|
+
|
|
182
|
+
if not local_path.exists():
|
|
183
|
+
logger.info(
|
|
184
|
+
f"Local path {local_path} does not exist and gh url failed, exiting"
|
|
185
|
+
)
|
|
186
|
+
sys.exit(1)
|
|
187
|
+
|
|
188
|
+
# Add branch to github_url if available
|
|
189
|
+
if current_branch:
|
|
190
|
+
published_source = {"git": github_url, "rev": current_branch}
|
|
191
|
+
else:
|
|
192
|
+
published_source = {"git": github_url}
|
|
193
|
+
|
|
194
|
+
local_source = {"path": str(local_path), "editable": True}
|
|
195
|
+
|
|
196
|
+
if force_local or (not force_published and "git" in current_source):
|
|
197
|
+
new_source = local_source
|
|
198
|
+
if not local_path.exists():
|
|
199
|
+
logger.info(f"Local path {local_path} does not exist")
|
|
200
|
+
clone_repo(github_url, local_path)
|
|
201
|
+
else:
|
|
202
|
+
new_source = published_source
|
|
203
|
+
|
|
204
|
+
sources[module_name] = new_source
|
|
205
|
+
|
|
206
|
+
# Write back with preserved comments
|
|
207
|
+
with open(pyproject_path, "w") as f:
|
|
208
|
+
tomlkit.dump(config, f)
|
|
209
|
+
|
|
210
|
+
logger.info(f"Set {module_name} source to: {new_source}")
|
|
211
|
+
|
|
212
|
+
# TODO should run something like, but make it more isolated since this will drop other groups that were previously installed
|
|
213
|
+
# uv sync --upgrade-package starlette-context
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def main():
|
|
217
|
+
parser = argparse.ArgumentParser()
|
|
218
|
+
parser.add_argument("module", help="Module name to toggle")
|
|
219
|
+
parser.add_argument("--local", action="store_true", help="Force local path")
|
|
220
|
+
parser.add_argument(
|
|
221
|
+
"--published",
|
|
222
|
+
action="store_true",
|
|
223
|
+
help="Force published github source",
|
|
224
|
+
)
|
|
225
|
+
parser.add_argument(
|
|
226
|
+
"--pypi",
|
|
227
|
+
action="store_true",
|
|
228
|
+
help="Force PyPI published version",
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
args = parser.parse_args()
|
|
232
|
+
toggle_module_source(args.module, args.local, args.published, args.pypi)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
if __name__ == "__main__":
|
|
236
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
revision = 1
|
|
3
|
+
requires-python = ">=3.9"
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "colorama"
|
|
7
|
+
version = "0.4.6"
|
|
8
|
+
source = { registry = "https://pypi.org/simple" }
|
|
9
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
|
|
10
|
+
wheels = [
|
|
11
|
+
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "exceptiongroup"
|
|
16
|
+
version = "1.2.2"
|
|
17
|
+
source = { registry = "https://pypi.org/simple" }
|
|
18
|
+
sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
|
|
19
|
+
wheels = [
|
|
20
|
+
{ url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[[package]]
|
|
24
|
+
name = "iniconfig"
|
|
25
|
+
version = "2.0.0"
|
|
26
|
+
source = { registry = "https://pypi.org/simple" }
|
|
27
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
|
|
28
|
+
wheels = [
|
|
29
|
+
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[[package]]
|
|
33
|
+
name = "packaging"
|
|
34
|
+
version = "24.2"
|
|
35
|
+
source = { registry = "https://pypi.org/simple" }
|
|
36
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
|
|
37
|
+
wheels = [
|
|
38
|
+
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[[package]]
|
|
42
|
+
name = "pluggy"
|
|
43
|
+
version = "1.5.0"
|
|
44
|
+
source = { registry = "https://pypi.org/simple" }
|
|
45
|
+
sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
|
|
46
|
+
wheels = [
|
|
47
|
+
{ url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[[package]]
|
|
51
|
+
name = "pytest"
|
|
52
|
+
version = "8.3.5"
|
|
53
|
+
source = { registry = "https://pypi.org/simple" }
|
|
54
|
+
dependencies = [
|
|
55
|
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
56
|
+
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
|
57
|
+
{ name = "iniconfig" },
|
|
58
|
+
{ name = "packaging" },
|
|
59
|
+
{ name = "pluggy" },
|
|
60
|
+
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
61
|
+
]
|
|
62
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 }
|
|
63
|
+
wheels = [
|
|
64
|
+
{ url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 },
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[[package]]
|
|
68
|
+
name = "tomli"
|
|
69
|
+
version = "2.2.1"
|
|
70
|
+
source = { registry = "https://pypi.org/simple" }
|
|
71
|
+
sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 }
|
|
72
|
+
wheels = [
|
|
73
|
+
{ url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 },
|
|
74
|
+
{ url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 },
|
|
75
|
+
{ url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 },
|
|
76
|
+
{ url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 },
|
|
77
|
+
{ url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 },
|
|
78
|
+
{ url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 },
|
|
79
|
+
{ url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 },
|
|
80
|
+
{ url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 },
|
|
81
|
+
{ url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 },
|
|
82
|
+
{ url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 },
|
|
83
|
+
{ url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 },
|
|
84
|
+
{ url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 },
|
|
85
|
+
{ url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 },
|
|
86
|
+
{ url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 },
|
|
87
|
+
{ url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 },
|
|
88
|
+
{ url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 },
|
|
89
|
+
{ url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 },
|
|
90
|
+
{ url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 },
|
|
91
|
+
{ url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 },
|
|
92
|
+
{ url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 },
|
|
93
|
+
{ url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 },
|
|
94
|
+
{ url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 },
|
|
95
|
+
{ url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 },
|
|
96
|
+
{ url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 },
|
|
97
|
+
{ url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 },
|
|
98
|
+
{ url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 },
|
|
99
|
+
{ url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 },
|
|
100
|
+
{ url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 },
|
|
101
|
+
{ url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 },
|
|
102
|
+
{ url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 },
|
|
103
|
+
{ url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 },
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
[[package]]
|
|
107
|
+
name = "tomlkit"
|
|
108
|
+
version = "0.13.2"
|
|
109
|
+
source = { registry = "https://pypi.org/simple" }
|
|
110
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 }
|
|
111
|
+
wheels = [
|
|
112
|
+
{ url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 },
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
[[package]]
|
|
116
|
+
name = "uv-development-toggle"
|
|
117
|
+
version = "0.2.0"
|
|
118
|
+
source = { editable = "." }
|
|
119
|
+
dependencies = [
|
|
120
|
+
{ name = "tomlkit" },
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
[package.dev-dependencies]
|
|
124
|
+
dev = [
|
|
125
|
+
{ name = "pytest" },
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
[package.metadata]
|
|
129
|
+
requires-dist = [{ name = "tomlkit", specifier = ">=0.13.2" }]
|
|
130
|
+
|
|
131
|
+
[package.metadata.requires-dev]
|
|
132
|
+
dev = [{ name = "pytest", specifier = ">=8.3.3" }]
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from urllib.request import urlopen
|
|
9
|
+
|
|
10
|
+
import tomlkit
|
|
11
|
+
|
|
12
|
+
logging.basicConfig(
|
|
13
|
+
level=os.environ.get("LOG_LEVEL", "INFO").upper(),
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_github_username() -> str:
|
|
20
|
+
logger.debug("Attempting to get GitHub username")
|
|
21
|
+
# Try gh cli first
|
|
22
|
+
try:
|
|
23
|
+
result = subprocess.run(["gh", "api", "user"], capture_output=True, text=True)
|
|
24
|
+
if result.returncode == 0:
|
|
25
|
+
username = json.loads(result.stdout)["login"]
|
|
26
|
+
logger.debug(f"Found username via gh cli: {username}")
|
|
27
|
+
return username
|
|
28
|
+
except FileNotFoundError:
|
|
29
|
+
logger.debug("gh cli not found, trying git config")
|
|
30
|
+
|
|
31
|
+
# Fall back to git config
|
|
32
|
+
try:
|
|
33
|
+
result = subprocess.run(
|
|
34
|
+
["git", "config", "user.name"], capture_output=True, text=True
|
|
35
|
+
)
|
|
36
|
+
if result.returncode == 0:
|
|
37
|
+
username = result.stdout.strip()
|
|
38
|
+
logger.debug(f"Found username via git config: {username}")
|
|
39
|
+
return username
|
|
40
|
+
except FileNotFoundError:
|
|
41
|
+
logger.debug("git not found")
|
|
42
|
+
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def check_github_repo_exists(username: str, repo: str) -> bool:
|
|
47
|
+
logger.debug(f"Checking if repo exists: {username}/{repo}")
|
|
48
|
+
try:
|
|
49
|
+
urlopen(f"https://github.com/{username}/{repo}")
|
|
50
|
+
logger.debug("Repository found")
|
|
51
|
+
return True
|
|
52
|
+
except:
|
|
53
|
+
logger.debug("Repository not found")
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_pypi_info(package_name: str) -> dict:
|
|
58
|
+
logger.debug(f"Fetching PyPI data for {package_name}")
|
|
59
|
+
try:
|
|
60
|
+
with urlopen(f"https://pypi.org/pypi/{package_name}/json") as response:
|
|
61
|
+
data = json.loads(response.read())
|
|
62
|
+
logger.debug("Successfully fetched PyPI data")
|
|
63
|
+
return data
|
|
64
|
+
except:
|
|
65
|
+
logger.debug("Failed to fetch PyPI data")
|
|
66
|
+
return {}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_pypi_homepage(package_name: str) -> str:
|
|
70
|
+
data = get_pypi_info(package_name)
|
|
71
|
+
homepage = data.get("info", {}).get("home_page", "")
|
|
72
|
+
|
|
73
|
+
# Ensure homepage is not None
|
|
74
|
+
homepage = homepage or ""
|
|
75
|
+
|
|
76
|
+
# Check if homepage contains github.com
|
|
77
|
+
if homepage and "github.com" in homepage:
|
|
78
|
+
return homepage
|
|
79
|
+
|
|
80
|
+
# Check all project URLs for GitHub links
|
|
81
|
+
project_urls = data.get("info", {}).get("project_urls") or {}
|
|
82
|
+
|
|
83
|
+
# First try the repository link as priority
|
|
84
|
+
if (
|
|
85
|
+
"repository" in project_urls
|
|
86
|
+
and project_urls["repository"]
|
|
87
|
+
and "github.com" in project_urls["repository"]
|
|
88
|
+
):
|
|
89
|
+
return project_urls["repository"]
|
|
90
|
+
|
|
91
|
+
# Then look in all other project links
|
|
92
|
+
for url_name, url in project_urls.items():
|
|
93
|
+
if url and "github.com" in url:
|
|
94
|
+
return url
|
|
95
|
+
|
|
96
|
+
# Return homepage even if it's not a GitHub URL, or empty string
|
|
97
|
+
return homepage
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def clone_repo(github_url: str, target_path: Path):
|
|
101
|
+
logger.info(f"Cloning {github_url} into {target_path}")
|
|
102
|
+
subprocess.run(["git", "clone", github_url, str(target_path)], check=True)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_current_branch(repo_path: Path) -> str:
|
|
106
|
+
result = subprocess.run(
|
|
107
|
+
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
108
|
+
capture_output=True,
|
|
109
|
+
text=True,
|
|
110
|
+
cwd=str(repo_path),
|
|
111
|
+
)
|
|
112
|
+
return result.stdout.strip()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def toggle_module_source(
|
|
116
|
+
module_name: str,
|
|
117
|
+
force_local: bool = False,
|
|
118
|
+
force_published: bool = False,
|
|
119
|
+
force_pypi: bool = False,
|
|
120
|
+
):
|
|
121
|
+
pyproject_path = Path("pyproject.toml")
|
|
122
|
+
|
|
123
|
+
# Check if the pyproject.toml exists
|
|
124
|
+
if not pyproject_path.exists():
|
|
125
|
+
logger.error("No pyproject.toml found, are you in the right folder?")
|
|
126
|
+
sys.exit(1)
|
|
127
|
+
|
|
128
|
+
# Read with tomlkit to preserve comments and structure
|
|
129
|
+
with open(pyproject_path) as f:
|
|
130
|
+
config = tomlkit.load(f)
|
|
131
|
+
|
|
132
|
+
sources = config["tool"]["uv"]["sources"]
|
|
133
|
+
current_source = sources.get(module_name, {})
|
|
134
|
+
|
|
135
|
+
# Handle PyPI option
|
|
136
|
+
if force_pypi:
|
|
137
|
+
# For PyPI, we remove the source entry or set it to {} to use default PyPI source
|
|
138
|
+
if module_name in sources:
|
|
139
|
+
logger.info(f"Removing custom source for {module_name} to use PyPI version")
|
|
140
|
+
del sources[module_name]
|
|
141
|
+
else:
|
|
142
|
+
logger.info(f"Already using PyPI version for {module_name}")
|
|
143
|
+
|
|
144
|
+
# Write back with preserved comments
|
|
145
|
+
with open(pyproject_path, "w") as f:
|
|
146
|
+
tomlkit.dump(config, f)
|
|
147
|
+
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
dev_toggle_dir = os.environ.get("PYTHON_DEVELOPMENT_TOGGLE", "pypi")
|
|
151
|
+
local_path_default = Path(f"{dev_toggle_dir}/{module_name}")
|
|
152
|
+
local_path_dash = Path(f"{dev_toggle_dir}/{module_name.replace('_', '-')}")
|
|
153
|
+
local_path_underscore = Path(f"{dev_toggle_dir}/{module_name.replace('-', '_')}")
|
|
154
|
+
|
|
155
|
+
if local_path_default.exists():
|
|
156
|
+
local_path = local_path_default
|
|
157
|
+
elif local_path_dash.exists():
|
|
158
|
+
local_path = local_path_dash
|
|
159
|
+
elif local_path_underscore.exists():
|
|
160
|
+
local_path = local_path_underscore
|
|
161
|
+
else:
|
|
162
|
+
local_path = local_path_default
|
|
163
|
+
|
|
164
|
+
# Get current branch if local repo exists
|
|
165
|
+
current_branch = None
|
|
166
|
+
if local_path.exists():
|
|
167
|
+
current_branch = get_current_branch(local_path)
|
|
168
|
+
if current_branch in ("master", "main"):
|
|
169
|
+
current_branch = None
|
|
170
|
+
|
|
171
|
+
# Try to find the correct GitHub source
|
|
172
|
+
github_url = None
|
|
173
|
+
username = get_github_username()
|
|
174
|
+
|
|
175
|
+
if username and check_github_repo_exists(username, module_name):
|
|
176
|
+
github_url = f"https://github.com/{username}/{module_name}.git"
|
|
177
|
+
else:
|
|
178
|
+
pypi_homepage = get_pypi_homepage(module_name)
|
|
179
|
+
if "github.com" in pypi_homepage:
|
|
180
|
+
github_url = f"{pypi_homepage}.git"
|
|
181
|
+
|
|
182
|
+
if not github_url:
|
|
183
|
+
logger.warning(f"Could not determine GitHub URL for {module_name}")
|
|
184
|
+
|
|
185
|
+
if not local_path.exists():
|
|
186
|
+
logger.info(
|
|
187
|
+
f"Local path {local_path} does not exist and gh url failed, exiting"
|
|
188
|
+
)
|
|
189
|
+
sys.exit(1)
|
|
190
|
+
|
|
191
|
+
# Add branch to github_url if available
|
|
192
|
+
if current_branch:
|
|
193
|
+
published_source = {"git": github_url, "rev": current_branch}
|
|
194
|
+
else:
|
|
195
|
+
published_source = {"git": github_url}
|
|
196
|
+
|
|
197
|
+
local_source = {"path": str(local_path), "editable": True}
|
|
198
|
+
|
|
199
|
+
if force_local or (not force_published and "git" in current_source):
|
|
200
|
+
new_source = local_source
|
|
201
|
+
if not local_path.exists():
|
|
202
|
+
logger.info(f"Local path {local_path} does not exist")
|
|
203
|
+
clone_repo(github_url, local_path)
|
|
204
|
+
else:
|
|
205
|
+
new_source = published_source
|
|
206
|
+
|
|
207
|
+
sources[module_name] = new_source
|
|
208
|
+
|
|
209
|
+
# Write back with preserved comments
|
|
210
|
+
with open(pyproject_path, "w") as f:
|
|
211
|
+
tomlkit.dump(config, f)
|
|
212
|
+
|
|
213
|
+
logger.info(f"Set {module_name} source to: {new_source}")
|
|
214
|
+
|
|
215
|
+
# TODO should run something like, but make it more isolated since this will drop other groups that were previously installed
|
|
216
|
+
# uv sync --upgrade-package starlette-context
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def main():
|
|
220
|
+
parser = argparse.ArgumentParser()
|
|
221
|
+
parser.add_argument("module", help="Module name in pyproject.toml to toggle")
|
|
222
|
+
parser.add_argument(
|
|
223
|
+
"--local",
|
|
224
|
+
action="store_true",
|
|
225
|
+
help="Use local editable path, and clone repo if necessary",
|
|
226
|
+
)
|
|
227
|
+
parser.add_argument(
|
|
228
|
+
"--published",
|
|
229
|
+
action="store_true",
|
|
230
|
+
help="Use github source",
|
|
231
|
+
)
|
|
232
|
+
parser.add_argument(
|
|
233
|
+
"--pypi",
|
|
234
|
+
action="store_true",
|
|
235
|
+
help="Use PyPI published version",
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
args = parser.parse_args()
|
|
239
|
+
toggle_module_source(args.module, args.local, args.published, args.pypi)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
if __name__ == "__main__":
|
|
243
|
+
main()
|