Skip to content

Commit 29951bc

Browse files
committed
Refactor capture-django-settings to use manage.py shell
- Change implementation to use subprocess with 'python manage.py shell' - Add --manage-py option to specify path to manage.py file - Remove Django as optional dependency (no longer imported directly) - Update README with new usage instructions and requirements - More Django-idiomatic approach using manage.py instead of direct imports
1 parent 4651401 commit 29951bc

3 files changed

Lines changed: 88 additions & 54 deletions

File tree

config-utils/README.md

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,6 @@ Or install in editable mode for development:
3131
pip install -e .
3232
```
3333

34-
### For Django support
35-
36-
Install with Django optional dependencies:
37-
38-
```bash
39-
pip install ".[django]"
40-
```
4134

4235
## Usage
4336

@@ -68,9 +61,11 @@ config-utils capture-env -o config.yml -f yml
6861

6962
### Capture Django Settings
7063

71-
Capture Django project settings to a YAML file:
64+
Capture Django project settings to a YAML file using `python manage.py shell`:
7265

7366
```bash
67+
# Run from your Django project directory
68+
cd /path/to/your/django/project
7469
config-utils capture-django-settings
7570
```
7671

@@ -80,43 +75,57 @@ This will create `django_settings.yaml` with all Django settings.
8075

8176
- `-o, --output PATH`: Specify output file path (default: `django_settings.yaml`)
8277
- `-f, --format`: Output format, yaml or yml (default: `yaml`)
78+
- `-m, --manage-py PATH`: Path to manage.py (default: `manage.py`)
8379
- `-s, --settings`: Django settings module (e.g., `myproject.settings`)
8480

8581
#### Examples
8682

8783
```bash
88-
# Using DJANGO_SETTINGS_MODULE environment variable
89-
export DJANGO_SETTINGS_MODULE=myproject.settings
84+
# From Django project root directory
9085
config-utils capture-django-settings
9186

9287
# Specifying settings module via command line
9388
config-utils capture-django-settings -s myproject.settings
9489

9590
# Custom output file
96-
config-utils capture-django-settings -o my_django_config.yaml -s myproject.settings
91+
config-utils capture-django-settings -o my_django_config.yaml
92+
93+
# Specify manage.py path if not in current directory
94+
config-utils capture-django-settings -m /path/to/manage.py
95+
96+
# Using DJANGO_SETTINGS_MODULE environment variable
97+
export DJANGO_SETTINGS_MODULE=myproject.settings
98+
config-utils capture-django-settings
9799
```
98100

101+
**Note**: This command must be run from your Django project directory or you must specify the path to `manage.py` using the `--manage-py` option.
102+
99103
### Using with uvx
100104

101105
You can run the tool directly without installation:
102106

103107
```bash
104-
# From the project directory
108+
# Capture environment variables
105109
uvx --from . config-utils capture-env
106110

107111
# With options
108112
uvx --from . config-utils capture-env -o custom.yaml
109113

110-
# Django settings
111-
uvx --from . config-utils capture-django-settings -s myproject.settings
114+
# Django settings (from Django project directory)
115+
cd /path/to/django/project
116+
uvx --from /path/to/config-utils config-utils capture-django-settings
117+
118+
# Or specify manage.py path
119+
uvx --from /path/to/config-utils config-utils capture-django-settings -m /path/to/manage.py
112120
```
113121

114122
## Requirements
115123

116124
- Python >= 3.8
117125
- click >= 8.0.0
118126
- pyyaml >= 6.0
119-
- Django >= 3.2 (optional, for capture-django-settings)
127+
128+
**For Django settings capture**: The command uses `python manage.py shell`, so Django must be installed in your Django project's environment. The config-utils tool itself does not need Django as a dependency.
120129

121130
## Development
122131

config-utils/config_utils/cli.py

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import os
44
import sys
55
import yaml
6+
import subprocess
7+
import json
68
from pathlib import Path
79
import click
810

@@ -64,60 +66,83 @@ def capture_env(output, format):
6466
default='yaml',
6567
help='Output format (default: yaml)',
6668
)
69+
@click.option(
70+
'--manage-py',
71+
'-m',
72+
default='manage.py',
73+
help='Path to manage.py (default: manage.py)',
74+
type=click.Path(exists=True),
75+
)
6776
@click.option(
6877
'--settings',
6978
'-s',
7079
help='Django settings module (e.g., myproject.settings)',
7180
envvar='DJANGO_SETTINGS_MODULE',
7281
)
73-
def capture_django_settings(output, format, settings):
82+
def capture_django_settings(output, format, manage_py, settings):
7483
"""Capture Django settings and store them in YAML format.
7584
76-
Requires Django to be installed and DJANGO_SETTINGS_MODULE to be set,
77-
or pass it via --settings option.
85+
Uses 'python manage.py shell' to access Django settings.
86+
Requires manage.py to be present in the current directory or specify path with --manage-py.
7887
"""
7988
try:
80-
# Set Django settings module if provided
81-
if settings:
82-
os.environ['DJANGO_SETTINGS_MODULE'] = settings
83-
84-
# Check if DJANGO_SETTINGS_MODULE is set
85-
if 'DJANGO_SETTINGS_MODULE' not in os.environ:
89+
# Check if manage.py exists
90+
manage_path = Path(manage_py)
91+
if not manage_path.exists():
8692
click.echo(
87-
"✗ Error: DJANGO_SETTINGS_MODULE not set. "
88-
"Use --settings option or set the environment variable.",
93+
f"✗ Error: manage.py not found at {manage_path}. "
94+
"Run this command from your Django project root or use --manage-py to specify the path.",
8995
err=True
9096
)
9197
sys.exit(1)
9298

93-
# Import Django
99+
# Python script to run in Django shell
100+
django_script = """
101+
import json
102+
from django.conf import settings
103+
104+
settings_dict = {}
105+
for setting in dir(settings):
106+
if setting.isupper():
94107
try:
95-
import django
96-
from django.conf import settings as django_settings
97-
except ImportError:
98-
click.echo(
99-
"✗ Error: Django is not installed. "
100-
"Install it with: pip install django",
101-
err=True
102-
)
108+
value = getattr(settings, setting)
109+
# Convert non-serializable types to strings
110+
if not isinstance(value, (str, int, float, bool, list, dict, type(None))):
111+
value = str(value)
112+
settings_dict[setting] = value
113+
except Exception as e:
114+
settings_dict[setting] = f"<Error retrieving value: {str(e)}>"
115+
116+
print(json.dumps(settings_dict))
117+
"""
118+
119+
# Prepare environment variables
120+
env = os.environ.copy()
121+
if settings:
122+
env['DJANGO_SETTINGS_MODULE'] = settings
123+
124+
# Run manage.py shell with the script
125+
result = subprocess.run(
126+
['python', str(manage_path), 'shell'],
127+
input=django_script,
128+
capture_output=True,
129+
text=True,
130+
env=env,
131+
timeout=30
132+
)
133+
134+
if result.returncode != 0:
135+
click.echo(f"✗ Error running Django shell:", err=True)
136+
click.echo(result.stderr, err=True)
103137
sys.exit(1)
104138

105-
# Setup Django
106-
django.setup()
107-
108-
# Get all Django settings
109-
settings_dict = {}
110-
for setting in dir(django_settings):
111-
# Skip private/magic attributes
112-
if setting.isupper():
113-
try:
114-
value = getattr(django_settings, setting)
115-
# Convert non-serializable types to strings
116-
if not isinstance(value, (str, int, float, bool, list, dict, type(None))):
117-
value = str(value)
118-
settings_dict[setting] = value
119-
except Exception as e:
120-
settings_dict[setting] = f"<Error retrieving value: {str(e)}>"
139+
# Parse JSON output
140+
try:
141+
settings_dict = json.loads(result.stdout.strip())
142+
except json.JSONDecodeError:
143+
click.echo(f"✗ Error: Could not parse Django settings output", err=True)
144+
click.echo(f"Output: {result.stdout}", err=True)
145+
sys.exit(1)
121146

122147
# Ensure output path is Path object
123148
output_path = Path(output)
@@ -128,6 +153,9 @@ def capture_django_settings(output, format, settings):
128153

129154
click.echo(f"✓ Captured {len(settings_dict)} Django settings to {output_path}")
130155

156+
except subprocess.TimeoutExpired:
157+
click.echo("✗ Error: Django shell command timed out", err=True)
158+
sys.exit(1)
131159
except Exception as e:
132160
click.echo(f"✗ Error: {str(e)}", err=True)
133161
sys.exit(1)

config-utils/pyproject.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ dependencies = [
1313
"pyyaml>=6.0",
1414
]
1515

16-
[project.optional-dependencies]
17-
django = ["django>=3.2"]
18-
1916
[project.scripts]
2017
config-utils = "config_utils.cli:main"
2118

0 commit comments

Comments
 (0)