python-skills 1.0.0__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.
- python_skills/__init__.py +10 -0
- python_skills/__main__.py +6 -0
- python_skills/adapters/__init__.py +48 -0
- python_skills/adapters/agent_skills.py +415 -0
- python_skills/adapters/aider_adapter.py +226 -0
- python_skills/adapters/base.py +153 -0
- python_skills/adapters/claude.py +474 -0
- python_skills/adapters/cline.py +332 -0
- python_skills/adapters/codex.py +24 -0
- python_skills/adapters/continue_adapter.py +198 -0
- python_skills/adapters/cursor.py +327 -0
- python_skills/adapters/gemini.py +26 -0
- python_skills/adapters/goose.py +26 -0
- python_skills/adapters/junie.py +25 -0
- python_skills/adapters/kiro.py +382 -0
- python_skills/adapters/opencode.py +27 -0
- python_skills/adapters/roo.py +25 -0
- python_skills/adapters/universal.py +203 -0
- python_skills/adapters/vscode.py +27 -0
- python_skills/adapters/windsurf.py +26 -0
- python_skills/adapters/zed.py +27 -0
- python_skills/cli.py +326 -0
- python_skills/config.py +160 -0
- python_skills/detector.py +152 -0
- python_skills/installer.py +163 -0
- python_skills/markers.py +115 -0
- python_skills/skills/__init__.py +14 -0
- python_skills/skills/loader.py +171 -0
- python_skills/skills/metadata.py +152 -0
- python_skills/skills/registry.py +101 -0
- python_skills/state.py +204 -0
- python_skills-1.0.0.dist-info/METADATA +99 -0
- python_skills-1.0.0.dist-info/RECORD +105 -0
- python_skills-1.0.0.dist-info/WHEEL +4 -0
- python_skills-1.0.0.dist-info/entry_points.txt +2 -0
- python_skills-1.0.0.dist-info/licenses/LICENSE +21 -0
- skills/advanced_python.md +239 -0
- skills/anti_patterns/index.md +406 -0
- skills/comprehensions.md +167 -0
- skills/control_flow.md +175 -0
- skills/data_structures.md +243 -0
- skills/debugging/common_bugs.md +222 -0
- skills/debugging/inspection_techniques.md +249 -0
- skills/debugging/root_cause.md +203 -0
- skills/engineering/application_logging.md +195 -0
- skills/engineering/cli_apps.md +207 -0
- skills/engineering/configuration.md +218 -0
- skills/engineering/database.md +240 -0
- skills/engineering/dependency_management.md +205 -0
- skills/engineering/http_clients.md +267 -0
- skills/engineering/modules_packages.md +211 -0
- skills/engineering/packaging.md +197 -0
- skills/engineering/project_structure.md +155 -0
- skills/engineering/pyproject_toml.md +302 -0
- skills/engineering/virtual_environments.md +206 -0
- skills/functions.md +244 -0
- skills/generation/async_concurrency.md +291 -0
- skills/generation/error_handling.md +276 -0
- skills/generation/protocols_generics.md +243 -0
- skills/generation/type_hints.md +290 -0
- skills/generation/validation_pipeline.md +274 -0
- skills/generation/workflow.md +190 -0
- skills/oop.md +228 -0
- skills/quality/abstractions.md +154 -0
- skills/quality/comments.md +177 -0
- skills/quality/documentation.md +176 -0
- skills/quality/duplication.md +137 -0
- skills/quality/maintainability.md +142 -0
- skills/quality/naming.md +171 -0
- skills/quality/quality_functions.md +245 -0
- skills/quality/readability.md +239 -0
- skills/quality/type_annotations.md +192 -0
- skills/refactoring/behavior_preservation.md +157 -0
- skills/refactoring/incremental.md +187 -0
- skills/refactoring/interface_stability.md +199 -0
- skills/refactoring/safe_refactoring.md +206 -0
- skills/security/auth_boundaries.md +200 -0
- skills/security/command_injection.md +207 -0
- skills/security/dependency_risks.md +282 -0
- skills/security/file_handling.md +156 -0
- skills/security/input_validation.md +190 -0
- skills/security/path_traversal.md +172 -0
- skills/security/secrets.md +171 -0
- skills/security/sql_injection.md +188 -0
- skills/security/unsafe_deserialization.md +164 -0
- skills/stdlib/argparse.md +178 -0
- skills/stdlib/collections.md +212 -0
- skills/stdlib/datetime.md +187 -0
- skills/stdlib/functools.md +238 -0
- skills/stdlib/itertools.md +183 -0
- skills/stdlib/json.md +162 -0
- skills/stdlib/logging.md +185 -0
- skills/stdlib/os_sys.md +184 -0
- skills/stdlib/pathlib.md +218 -0
- skills/stdlib/re.md +171 -0
- skills/stdlib/statistics.md +112 -0
- skills/stdlib/subprocess.md +211 -0
- skills/testing/async_tests.md +249 -0
- skills/testing/coverage.md +168 -0
- skills/testing/edge_cases.md +197 -0
- skills/testing/fixtures_mocks.md +203 -0
- skills/testing/organization.md +205 -0
- skills/testing/parameterized.md +174 -0
- skills/testing/regression_tests.md +165 -0
- skills/variables_types.md +107 -0
skills/stdlib/json.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Stdlib: json
|
|
2
|
+
|
|
3
|
+
**Purpose**: JSON encoding/decoding with Python objects.
|
|
4
|
+
|
|
5
|
+
**When to use**: Serialization, config, APIs, data exchange.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Rules
|
|
10
|
+
|
|
11
|
+
### Basic Usage
|
|
12
|
+
```python
|
|
13
|
+
import json
|
|
14
|
+
|
|
15
|
+
# Encode
|
|
16
|
+
json_str = json.dumps(obj, indent=2, ensure_ascii=False)
|
|
17
|
+
json_bytes = json.dumps(obj).encode("utf-8")
|
|
18
|
+
|
|
19
|
+
# Decode
|
|
20
|
+
obj = json.loads(json_str)
|
|
21
|
+
obj = json.load(file_obj)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Default Serialization
|
|
25
|
+
| Python | JSON |
|
|
26
|
+
|--------|------|
|
|
27
|
+
| `dict` | object |
|
|
28
|
+
| `list`, `tuple` | array |
|
|
29
|
+
| `str` | string |
|
|
30
|
+
| `int`, `float` | number |
|
|
31
|
+
| `True`, `False` | `true`, `false` |
|
|
32
|
+
| `None` | `null` |
|
|
33
|
+
|
|
34
|
+
### Custom Serialization
|
|
35
|
+
```python
|
|
36
|
+
# default= callable for unsupported types
|
|
37
|
+
json.dumps(obj, default=lambda o: o.isoformat() if hasattr(o, "isoformat") else str(o))
|
|
38
|
+
|
|
39
|
+
# Or subclass JSONEncoder
|
|
40
|
+
class CustomEncoder(json.JSONEncoder):
|
|
41
|
+
def default(self, obj):
|
|
42
|
+
if isinstance(obj, datetime):
|
|
43
|
+
return obj.isoformat()
|
|
44
|
+
if isinstance(obj, UUID):
|
|
45
|
+
return str(obj)
|
|
46
|
+
return super().default(obj)
|
|
47
|
+
|
|
48
|
+
json.dumps(obj, cls=CustomEncoder)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Custom Deserialization
|
|
52
|
+
```python
|
|
53
|
+
# object_hook for dicts
|
|
54
|
+
def decode_datetime(d):
|
|
55
|
+
for key, value in d.items():
|
|
56
|
+
if key.endswith("_at") and isinstance(value, str):
|
|
57
|
+
try:
|
|
58
|
+
d[key] = datetime.fromisoformat(value)
|
|
59
|
+
except ValueError:
|
|
60
|
+
pass
|
|
61
|
+
return d
|
|
62
|
+
|
|
63
|
+
json.loads(json_str, object_hook=decode_datetime)
|
|
64
|
+
|
|
65
|
+
# object_pairs_hook for ordered pairs (preserves duplicate keys)
|
|
66
|
+
json.loads(json_str, object_pairs_hook=OrderedDict)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Streaming (Large Data)
|
|
70
|
+
```python
|
|
71
|
+
# Incremental encoding
|
|
72
|
+
encoder = json.JSONEncoder()
|
|
73
|
+
for chunk in encoder.iterencode(large_obj):
|
|
74
|
+
write(chunk)
|
|
75
|
+
|
|
76
|
+
# Incremental decoding
|
|
77
|
+
decoder = json.JSONDecoder()
|
|
78
|
+
for obj in decoder.raw_decode(stream):
|
|
79
|
+
...
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Decision Rules
|
|
85
|
+
|
|
86
|
+
| Situation | Approach |
|
|
87
|
+
|-----------|----------|
|
|
88
|
+
| Simple dict/list | `json.dumps` / `json.loads` |
|
|
89
|
+
| Datetime, UUID, Decimal | Custom `JSONEncoder` + `object_hook` |
|
|
90
|
+
| Large data (streaming) | `JSONEncoder.iterencode` / `JSONDecoder.raw_decode` |
|
|
91
|
+
| Preserve order | Default (Python 3.7+) or `object_pairs_hook` |
|
|
92
|
+
| Non-ASCII chars | `ensure_ascii=False` |
|
|
93
|
+
| Compact output | `separators=(",", ":")` |
|
|
94
|
+
| Human-readable | `indent=2` |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Preferred Patterns
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
# Standard API response
|
|
102
|
+
def to_json(data: Any) -> str:
|
|
103
|
+
return json.dumps(
|
|
104
|
+
data,
|
|
105
|
+
default=json_default,
|
|
106
|
+
ensure_ascii=False,
|
|
107
|
+
separators=(",", ":"),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def json_default(obj):
|
|
111
|
+
if isinstance(obj, (datetime, date)):
|
|
112
|
+
return obj.isoformat()
|
|
113
|
+
if isinstance(obj, UUID):
|
|
114
|
+
return str(obj)
|
|
115
|
+
if isinstance(obj, Decimal):
|
|
116
|
+
return str(obj)
|
|
117
|
+
if hasattr(obj, "__dict__"):
|
|
118
|
+
return obj.__dict__
|
|
119
|
+
raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")
|
|
120
|
+
|
|
121
|
+
# Safe loading with schema validation
|
|
122
|
+
def load_json(path: Path, schema: type) -> Any:
|
|
123
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
124
|
+
# Validate with pydantic, attrs, or manual check
|
|
125
|
+
return validate(data, schema)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Security
|
|
131
|
+
|
|
132
|
+
- **Never** use `json.load` on untrusted input without validation
|
|
133
|
+
- `json` module is safe (no code execution) unlike `pickle`/`yaml`
|
|
134
|
+
- Large inputs can cause DoS (billions of nested objects) — consider `defusedxml`-style limits
|
|
135
|
+
- `object_hook` runs on every dict — avoid expensive operations
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Avoid
|
|
140
|
+
|
|
141
|
+
- `pickle` for data exchange (unsafe, Python-specific)
|
|
142
|
+
- `yaml.load` without `Loader=yaml.SafeLoader` (unsafe)
|
|
143
|
+
- Custom encoders that mutate input
|
|
144
|
+
- `default=str` (silently converts everything to string, loses type info)
|
|
145
|
+
- Large `indent` in production (wastes bandwidth)
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Validation Considerations
|
|
150
|
+
|
|
151
|
+
- Schema validation after decode (pydantic, jsonschema, manual)
|
|
152
|
+
- Round-trip test: `json.loads(json.dumps(obj)) == obj`
|
|
153
|
+
- `json.JSONDecodeError` for parse errors (includes position)
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Related Skills
|
|
158
|
+
|
|
159
|
+
- `security/unsafe_deserialization.md`
|
|
160
|
+
- `engineering/configuration.md`
|
|
161
|
+
- `stdlib/datetime.md`
|
|
162
|
+
- `generation/type_hints.md` (TypedDict for JSON shapes)
|
skills/stdlib/logging.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Stdlib: logging
|
|
2
|
+
|
|
3
|
+
**Purpose**: Structured application logging.
|
|
4
|
+
|
|
5
|
+
**When to use**: All application logging. Never `print()` in production code.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Rules
|
|
10
|
+
|
|
11
|
+
### Basic Setup
|
|
12
|
+
```python
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
# Module-level logger (standard pattern)
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
# Logging levels
|
|
19
|
+
logger.debug("Detailed diagnostic")
|
|
20
|
+
logger.info("General operation")
|
|
21
|
+
logger.warning("Unexpected, but handled")
|
|
22
|
+
logger.error("Function failed")
|
|
23
|
+
logger.critical("System may fail")
|
|
24
|
+
|
|
25
|
+
# With context
|
|
26
|
+
logger.info("User login", extra={"user_id": 123, "ip": "1.2.3.4"})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Configuration (Application Entry Point)
|
|
30
|
+
```python
|
|
31
|
+
import logging
|
|
32
|
+
import logging.config
|
|
33
|
+
|
|
34
|
+
LOGGING_CONFIG = {
|
|
35
|
+
"version": 1,
|
|
36
|
+
"disable_existing_loggers": False,
|
|
37
|
+
"formatters": {
|
|
38
|
+
"standard": {
|
|
39
|
+
"format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
40
|
+
"datefmt": "%Y-%m-%d %H:%M:%S",
|
|
41
|
+
},
|
|
42
|
+
"json": {
|
|
43
|
+
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
|
|
44
|
+
"format": "%(asctime)s %(levelname)s %(name)s %(message)s",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
"handlers": {
|
|
48
|
+
"console": {
|
|
49
|
+
"class": "logging.StreamHandler",
|
|
50
|
+
"formatter": "standard",
|
|
51
|
+
"stream": "ext://sys.stdout",
|
|
52
|
+
},
|
|
53
|
+
"file": {
|
|
54
|
+
"class": "logging.handlers.RotatingFileHandler",
|
|
55
|
+
"formatter": "json",
|
|
56
|
+
"filename": "app.log",
|
|
57
|
+
"maxBytes": 10_000_000,
|
|
58
|
+
"backupCount": 5,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
"root": {
|
|
62
|
+
"level": "INFO",
|
|
63
|
+
"handlers": ["console", "file"],
|
|
64
|
+
},
|
|
65
|
+
"loggers": {
|
|
66
|
+
"myapp": {"level": "DEBUG", "propagate": True},
|
|
67
|
+
"httpx": {"level": "WARNING"}, # Quiet noisy libs
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
logging.config.dictConfig(LOGGING_CONFIG)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Structured Logging (Recommended)
|
|
75
|
+
```python
|
|
76
|
+
# Use extra for structured data
|
|
77
|
+
logger.info("Order processed", extra={
|
|
78
|
+
"order_id": "ORD-123",
|
|
79
|
+
"amount": 99.99,
|
|
80
|
+
"currency": "USD",
|
|
81
|
+
"customer_id": "CUST-456",
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
# Or use a structured logging library (structlog)
|
|
85
|
+
import structlog
|
|
86
|
+
|
|
87
|
+
log = structlog.get_logger()
|
|
88
|
+
log.info("order_processed", order_id="ORD-123", amount=99.99)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Exception Logging
|
|
92
|
+
```python
|
|
93
|
+
try:
|
|
94
|
+
risky()
|
|
95
|
+
except Exception:
|
|
96
|
+
logger.exception("Operation failed") # Includes traceback
|
|
97
|
+
# Or:
|
|
98
|
+
logger.error("Operation failed", exc_info=True)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Lazy Evaluation (Performance)
|
|
102
|
+
```python
|
|
103
|
+
# Arguments evaluated only if level enabled
|
|
104
|
+
logger.debug("Expensive: %s", expensive_computation) # Not f-string!
|
|
105
|
+
|
|
106
|
+
# For complex objects
|
|
107
|
+
logger.debug("Data: %s", lambda: repr(large_object))
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Decision Rules
|
|
113
|
+
|
|
114
|
+
| Situation | Approach |
|
|
115
|
+
|-----------|----------|
|
|
116
|
+
| Application code | `logger = logging.getLogger(__name__)` |
|
|
117
|
+
| Library code | Same, but don't configure — let app configure |
|
|
118
|
+
| Structured data | `extra={...}` or `structlog` |
|
|
119
|
+
| Exceptions | `logger.exception()` or `exc_info=True` |
|
|
120
|
+
| High-volume debug | Lazy `%s` formatting |
|
|
121
|
+
| Production | JSON format, file rotation |
|
|
122
|
+
| Development | Human-readable, console |
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Preferred Patterns
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
# Library: just get logger, don't configure
|
|
130
|
+
# mylib/__init__.py
|
|
131
|
+
import logging
|
|
132
|
+
log = logging.getLogger(__name__)
|
|
133
|
+
|
|
134
|
+
# Application: configure once at startup
|
|
135
|
+
# main.py
|
|
136
|
+
def setup_logging(level: str = "INFO", json_format: bool = False):
|
|
137
|
+
import logging.config
|
|
138
|
+
# ... dictConfig as above
|
|
139
|
+
|
|
140
|
+
# Context injection (middleware, decorators)
|
|
141
|
+
def with_context(func):
|
|
142
|
+
@functools.wraps(func)
|
|
143
|
+
def wrapper(*args, **kwargs):
|
|
144
|
+
old_factory = logging.getLogRecordFactory()
|
|
145
|
+
def factory(*a, **kw):
|
|
146
|
+
record = old_factory(*a, **kw)
|
|
147
|
+
record.request_id = getattr(request_context, "id", None)
|
|
148
|
+
return record
|
|
149
|
+
logging.setLogRecordFactory(factory)
|
|
150
|
+
try:
|
|
151
|
+
return func(*args, **kwargs)
|
|
152
|
+
finally:
|
|
153
|
+
logging.setLogRecordFactory(old_factory)
|
|
154
|
+
return wrapper
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Avoid
|
|
160
|
+
|
|
161
|
+
- `print()` in production code
|
|
162
|
+
- `logging.basicConfig()` in libraries (configures root globally)
|
|
163
|
+
- `logging.getLogger()` without name (root logger)
|
|
164
|
+
- f-strings in logging calls (evaluates always)
|
|
165
|
+
- Logging sensitive data (secrets, PII, tokens)
|
|
166
|
+
- Excessive DEBUG in production
|
|
167
|
+
- Catching and logging then re-raising without context
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Validation Considerations
|
|
172
|
+
|
|
173
|
+
- Test log output format
|
|
174
|
+
- Verify log levels in different environments
|
|
175
|
+
- Check log rotation works
|
|
176
|
+
- Ensure no PII in logs (automated scanning)
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Related Skills
|
|
181
|
+
|
|
182
|
+
- `engineering/configuration.md`
|
|
183
|
+
- `security/secrets.md`
|
|
184
|
+
- `stdlib/os_sys.md` (environment config)
|
|
185
|
+
- `generation/error_handling.md` (exception logging)
|
skills/stdlib/os_sys.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Stdlib: os and sys
|
|
2
|
+
|
|
3
|
+
**Purpose**: Operating system interfaces and Python runtime.
|
|
4
|
+
|
|
5
|
+
**When to use**: Process environment, filesystem, Python internals. Prefer `pathlib` for paths.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Rules
|
|
10
|
+
|
|
11
|
+
### os — Environment and Process
|
|
12
|
+
```python
|
|
13
|
+
import os
|
|
14
|
+
|
|
15
|
+
# Environment variables
|
|
16
|
+
os.environ # Mapping (str -> str)
|
|
17
|
+
os.getenv("KEY", "default")
|
|
18
|
+
os.environ["KEY"] = "value" # Set
|
|
19
|
+
del os.environ["KEY"] # Unset
|
|
20
|
+
|
|
21
|
+
# Process
|
|
22
|
+
os.getpid() # Current PID
|
|
23
|
+
os.getppid() # Parent PID
|
|
24
|
+
os.cpu_count() # CPU count
|
|
25
|
+
os.urandom(n) # Cryptographic random bytes
|
|
26
|
+
|
|
27
|
+
# Working directory
|
|
28
|
+
os.getcwd() # Str
|
|
29
|
+
os.chdir(path) # Change
|
|
30
|
+
|
|
31
|
+
# Filesystem (prefer pathlib)
|
|
32
|
+
os.listdir(path) # Names only
|
|
33
|
+
os.scandir(path) # DirEntry objects (efficient)
|
|
34
|
+
os.makedirs(path, exist_ok=True)
|
|
35
|
+
os.remove(path)
|
|
36
|
+
os.rmdir(path)
|
|
37
|
+
os.rename(src, dst)
|
|
38
|
+
os.replace(src, dst) # Atomic
|
|
39
|
+
os.symlink(src, dst)
|
|
40
|
+
os.readlink(path)
|
|
41
|
+
os.stat(path) # stat_result
|
|
42
|
+
os.path.isfile(path) # Legacy — use pathlib
|
|
43
|
+
|
|
44
|
+
# Path manipulation (legacy — use pathlib)
|
|
45
|
+
os.path.join(*parts)
|
|
46
|
+
os.path.split(path)
|
|
47
|
+
os.path.dirname(path)
|
|
48
|
+
os.path.basename(path)
|
|
49
|
+
os.path.splitext(path)
|
|
50
|
+
os.path.abspath(path)
|
|
51
|
+
os.path.relpath(path, start)
|
|
52
|
+
os.path.normpath(path)
|
|
53
|
+
os.path.expanduser("~/path")
|
|
54
|
+
os.path.expandvars("$VAR/path")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### sys — Python Runtime
|
|
58
|
+
```python
|
|
59
|
+
import sys
|
|
60
|
+
|
|
61
|
+
# Version
|
|
62
|
+
sys.version # String
|
|
63
|
+
sys.version_info # Named tuple (major, minor, micro, ...)
|
|
64
|
+
sys.version_info >= (3, 12)
|
|
65
|
+
|
|
66
|
+
# Paths
|
|
67
|
+
sys.path # Module search path (list)
|
|
68
|
+
sys.prefix # Install prefix
|
|
69
|
+
sys.exec_prefix # Platform-specific prefix
|
|
70
|
+
sys.executable # Python interpreter path
|
|
71
|
+
|
|
72
|
+
# Arguments
|
|
73
|
+
sys.argv # CLI args (list[str])
|
|
74
|
+
sys.flags # Interpreter flags
|
|
75
|
+
|
|
76
|
+
# I/O
|
|
77
|
+
sys.stdin # TextIOWrapper
|
|
78
|
+
sys.stdout
|
|
79
|
+
sys.stderr
|
|
80
|
+
sys.stdin.buffer # Binary
|
|
81
|
+
sys.stdout.buffer
|
|
82
|
+
sys.stderr.buffer
|
|
83
|
+
|
|
84
|
+
# Exit
|
|
85
|
+
sys.exit(code) # SystemExit
|
|
86
|
+
sys.exit("message") # Prints to stderr, exit 1
|
|
87
|
+
|
|
88
|
+
# Modules
|
|
89
|
+
sys.modules # Loaded modules dict
|
|
90
|
+
sys.builtin_module_names
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Platform Detection
|
|
94
|
+
```python
|
|
95
|
+
import sys, os
|
|
96
|
+
|
|
97
|
+
sys.platform # "linux", "darwin", "win32", "cygwin"
|
|
98
|
+
os.name # "posix", "nt", "java"
|
|
99
|
+
|
|
100
|
+
# Better: platform module
|
|
101
|
+
import platform
|
|
102
|
+
platform.system() # "Linux", "Darwin", "Windows"
|
|
103
|
+
platform.release() # Kernel version
|
|
104
|
+
platform.machine() # "x86_64", "arm64"
|
|
105
|
+
platform.python_implementation() # "CPython", "PyPy"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Decision Rules
|
|
111
|
+
|
|
112
|
+
| Need | Module | Function |
|
|
113
|
+
|------|--------|----------|
|
|
114
|
+
| Env vars | `os` | `os.getenv`, `os.environ` |
|
|
115
|
+
| CWD | `os` | `os.getcwd`, `os.chdir` |
|
|
116
|
+
| CLI args | `sys` | `sys.argv` |
|
|
117
|
+
| Python version | `sys` | `sys.version_info` |
|
|
118
|
+
| Module path | `sys` | `sys.path` |
|
|
119
|
+
| Std I/O | `sys` | `sys.std*` |
|
|
120
|
+
| Exit | `sys` | `sys.exit` |
|
|
121
|
+
| Random bytes | `os` | `os.urandom` |
|
|
122
|
+
| Platform | `platform` | `platform.system()` |
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Preferred Patterns
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
# Environment-based config
|
|
130
|
+
def get_config() -> Config:
|
|
131
|
+
return Config(
|
|
132
|
+
database_url=os.getenv("DATABASE_URL", "sqlite:///local.db"),
|
|
133
|
+
debug=os.getenv("DEBUG", "false").lower() == "true",
|
|
134
|
+
port=int(os.getenv("PORT", "8000")),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Platform-specific paths
|
|
138
|
+
def get_data_dir() -> Path:
|
|
139
|
+
if sys.platform == "win32":
|
|
140
|
+
base = Path(os.getenv("LOCALAPPDATA", Path.home() / "AppData" / "Local"))
|
|
141
|
+
elif sys.platform == "darwin":
|
|
142
|
+
base = Path.home() / "Library" / "Application Support"
|
|
143
|
+
else: # Linux/Unix
|
|
144
|
+
base = Path(os.getenv("XDG_DATA_HOME", Path.home() / ".local" / "share"))
|
|
145
|
+
return base / "myapp"
|
|
146
|
+
|
|
147
|
+
# Safe subprocess with clean env
|
|
148
|
+
def run_clean(cmd: list[str]):
|
|
149
|
+
clean_env = {
|
|
150
|
+
"PATH": "/usr/bin:/bin",
|
|
151
|
+
"LANG": "C.UTF-8",
|
|
152
|
+
"HOME": "/tmp",
|
|
153
|
+
}
|
|
154
|
+
return subprocess.run(cmd, env=clean_env, ...)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Avoid
|
|
160
|
+
|
|
161
|
+
- `os.path` for new code (use `pathlib`)
|
|
162
|
+
- `os.system`, `os.popen` (use `subprocess`)
|
|
163
|
+
- Modifying `sys.path` at runtime (use proper packaging)
|
|
164
|
+
- `sys.exit()` in library code (raise exceptions)
|
|
165
|
+
- Hardcoding paths (`/tmp`, `C:\\Temp`)
|
|
166
|
+
- Assuming Unix paths on Windows
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Validation Considerations
|
|
171
|
+
|
|
172
|
+
- Test on target platforms (Windows vs Unix)
|
|
173
|
+
- Verify env var handling with missing/empty values
|
|
174
|
+
- Check path handling with spaces, unicode
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Related Skills
|
|
179
|
+
|
|
180
|
+
- `stdlib/pathlib.md`
|
|
181
|
+
- `stdlib/subprocess.md`
|
|
182
|
+
- `engineering/configuration.md`
|
|
183
|
+
- `engineering/virtual_environments.md`
|
|
184
|
+
- `security/secrets.md`
|