Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b6d861d82 | |||
| 493164d147 | |||
| 343c9b288e | |||
| e3937ed2fe | |||
| 2952c7f83c | |||
| fa1d0b129b | |||
| ef5f6c8397 | |||
| 28ff68068a | |||
| 80ebe6fd65 | |||
| 3eab860187 | |||
| 25d64157e7 | |||
| e0a72c46ff | |||
| 24f7b449da | |||
| cc230adc64 | |||
| 4e0a97f26f | |||
| e68c6102b3 | |||
| cbf233be79 | |||
| 291a6402b9 | |||
| 98638bd994 | |||
| 30b097fdcb | |||
| 4577239526 | |||
| 8c9e14cbd0 | |||
| d8a75ea395 | |||
| 1ca990875f | |||
| 98f32b7d28 | |||
| 436932c9da | |||
| cecd162641 | |||
| c95c651486 | |||
| 970cb0fd41 | |||
| 9de791ebb0 | |||
| 996f69b4ca | |||
| dc49ede69d | |||
| 98e85d8ac8 | |||
| 90d2a85c83 | |||
| 8ac026f0c9 | |||
| 2095217d0f | |||
| c0ee2091f6 | |||
| 1085fbe3ee | |||
| 459737a1b0 | |||
| 228e2c981b | |||
| 08e451198b | |||
| d87f1004ab | |||
| d682bc5e18 | |||
| f507a005a8 | |||
| c72195fa33 | |||
| 1093cc76bb | |||
| eda78b6530 | |||
| b3b3a54b90 | |||
| e16fa6b3d2 | |||
| e88b92e69f | |||
| 9fdf1e5c78 | |||
| 045aa09784 | |||
| c36345b842 | |||
| f2104c5041 | |||
| d23a9d6153 | |||
| bb69623fc6 | |||
| 3cb46dc1bd | |||
| 249eeaf4d5 | |||
| 83bbd58728 | |||
| d5deb809c3 | |||
| c77a8681f7 | |||
| 0fe190a913 | |||
| 99156ade28 | |||
| 44ab0ba1ec |
@@ -2,44 +2,57 @@ name: CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [ main, master ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [ main, master ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
checks: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
|
cache: 'pip'
|
||||||
|
|
||||||
|
- name: Install linting tools
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install ruff mypy
|
||||||
|
|
||||||
|
- name: Run ruff check
|
||||||
|
run: python -m ruff check config_convert tests
|
||||||
|
|
||||||
|
- name: Run ruff format check
|
||||||
|
run: python -m ruff format --check config_convert tests
|
||||||
|
|
||||||
|
- name: Run mypy type checking
|
||||||
|
run: python -m mypy config_convert tests
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
cache: 'pip'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install -e ".[dev]"
|
pip install -e ".[dev]"
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run pytest
|
||||||
run: pytest tests/ -v --tb=short
|
run: python -m pytest tests/ -v --tb=short --ignore=tests/integration/
|
||||||
|
|
||||||
- name: Run tests with coverage
|
|
||||||
run: pytest --cov=config_convert --cov-report=term-missing
|
|
||||||
|
|
||||||
lint:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: '3.11'
|
|
||||||
|
|
||||||
- name: Install lint tools
|
|
||||||
run: pip install ruff
|
|
||||||
|
|
||||||
- name: Run ruff
|
|
||||||
run: ruff check .
|
|
||||||
|
|||||||
141
README.md
141
README.md
@@ -1,6 +1,6 @@
|
|||||||
# ConfigConvert CLI
|
# ConfigConvert CLI
|
||||||
|
|
||||||
A powerful CLI tool for bidirectional conversion between JSON, YAML, TOML, and ENV config formats with smart type inference, validation, flatten/unflatten operations, schema generation, and automatic shell completion support.
|
A powerful CLI tool for bidirectional conversion between JSON, YAML, TOML, and ENV config formats.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -11,25 +11,14 @@ A powerful CLI tool for bidirectional conversion between JSON, YAML, TOML, and E
|
|||||||
- **Schema Generation**: Generate JSON Schema from parsed configurations
|
- **Schema Generation**: Generate JSON Schema from parsed configurations
|
||||||
- **Shell Completion**: Built-in support for bash, zsh, and fish completions
|
- **Shell Completion**: Built-in support for bash, zsh, and fish completions
|
||||||
- **Offline Operation**: Runs entirely offline with minimal dependencies
|
- **Offline Operation**: Runs entirely offline with minimal dependencies
|
||||||
- **Rich Output**: Beautiful terminal output using Rich library
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### From PyPI
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install config-convert-cli
|
pip install config-convert-cli
|
||||||
```
|
```
|
||||||
|
|
||||||
### From Source
|
## Usage
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://7000pct.gitea.bloupla.net/7000pctAUTO/config-convert-cli.git
|
|
||||||
cd config-convert-cli
|
|
||||||
pip install -e ".[dev]"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### Convert between formats
|
### Convert between formats
|
||||||
|
|
||||||
@@ -37,77 +26,55 @@ pip install -e ".[dev]"
|
|||||||
# Convert JSON to YAML
|
# Convert JSON to YAML
|
||||||
config-convert convert input.json --to yaml -o output.yaml
|
config-convert convert input.json --to yaml -o output.yaml
|
||||||
|
|
||||||
# Convert YAML to JSON with custom indentation
|
# Convert YAML to JSON
|
||||||
config-convert convert config.yaml --from yaml --to json --indent 4
|
config-convert convert config.yaml --from yaml --to json
|
||||||
|
|
||||||
# Convert ENV to JSON
|
# Convert ENV to JSON
|
||||||
config-convert convert .env --from env --to json
|
config-convert convert .env --from env --to json
|
||||||
|
|
||||||
# Read from stdin
|
|
||||||
cat config.json | config-convert convert --stdin --from json --to yaml
|
|
||||||
|
|
||||||
# Pretty print output
|
# Pretty print output
|
||||||
config-convert convert config.json --to yaml --indent 2
|
config-convert convert config.json --to yaml --pretty
|
||||||
```
|
```
|
||||||
|
|
||||||
### Validate syntax
|
### Validate syntax
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Validate a config file
|
config-validate config.yaml
|
||||||
config-convert validate config.yaml
|
|
||||||
|
|
||||||
# Validate from stdin
|
|
||||||
echo '{"name": "test"}' | config-convert validate --stdin --format json
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Flatten nested structures
|
### Flatten nested structures
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Flatten nested JSON to ENV format
|
|
||||||
config-convert flatten config.json --output flat.env
|
config-convert flatten config.json --output flat.env
|
||||||
|
|
||||||
# Flatten to another structured format
|
|
||||||
config-convert flatten config.json --format yaml
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Unflatten to nested structures
|
### Unflatten to nested structures
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Unflatten ENV to JSON
|
config-convert unflatten flat.env --output nested.yaml
|
||||||
config-convert unflatten flat.env --format json --output nested.json
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Generate JSON Schema
|
### Generate JSON Schema
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Generate schema from config
|
|
||||||
config-convert schema config.json -o schema.json
|
config-convert schema config.json -o schema.json
|
||||||
|
|
||||||
# Generate and display schema
|
|
||||||
config-convert schema config.json
|
|
||||||
```
|
|
||||||
|
|
||||||
### List supported formats
|
|
||||||
|
|
||||||
```bash
|
|
||||||
config-convert formats
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Supported Formats
|
## Supported Formats
|
||||||
|
|
||||||
| Format | Extensions | Read | Write |
|
| Format | Extension | Read | Write |
|
||||||
|--------|------------|------|-------|
|
|--------|-----------|------|-------|
|
||||||
| JSON | .json | Yes | Yes |
|
| JSON | .json | ✓ | ✓ |
|
||||||
| YAML | .yaml, .yml| Yes | Yes |
|
| YAML | .yaml | ✓ | ✓ |
|
||||||
| TOML | .toml | Yes | Yes |
|
| TOML | .toml | ✓ | ✓ |
|
||||||
| ENV | .env | Yes | Yes |
|
| ENV | .env | ✓ | ✓ |
|
||||||
|
|
||||||
## Shell Completion
|
## Shell Completion
|
||||||
|
|
||||||
### Bash
|
### Bash
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install completions for current user
|
# Install completions
|
||||||
config-convert --install-completion bash
|
config-convert --install-completion bash
|
||||||
|
|
||||||
# Or source directly
|
# Or source directly
|
||||||
@@ -126,15 +93,6 @@ config-convert --install-completion zsh
|
|||||||
config-convert --install-completion fish
|
config-convert --install-completion fish
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
| Variable | Description | Default |
|
|
||||||
|----------|-------------|---------|
|
|
||||||
| CONFIG_CONVERT_THEME | Color theme for Rich output | default |
|
|
||||||
| CONFIG_CONVERT_INDENT | Default indentation for output | 2 |
|
|
||||||
|
|
||||||
## Exit Codes
|
## Exit Codes
|
||||||
|
|
||||||
| Code | Description |
|
| Code | Description |
|
||||||
@@ -146,75 +104,6 @@ config-convert --install-completion fish
|
|||||||
| 4 | Syntax error in input |
|
| 4 | Syntax error in input |
|
||||||
| 5 | Conversion error |
|
| 5 | Conversion error |
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
### Setup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create virtual environment
|
|
||||||
python -m venv venv
|
|
||||||
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
pip install -e ".[dev]"
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
pytest tests/ -v
|
|
||||||
|
|
||||||
# Run with coverage
|
|
||||||
pytest --cov=config_convert --cov-report=term-missing
|
|
||||||
|
|
||||||
# Run linting
|
|
||||||
ruff check .
|
|
||||||
```
|
|
||||||
|
|
||||||
### Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
config-convert-cli/
|
|
||||||
├── config_convert/
|
|
||||||
│ ├── __init__.py
|
|
||||||
│ ├── cli.py
|
|
||||||
│ ├── converters/
|
|
||||||
│ │ ├── __init__.py
|
|
||||||
│ │ ├── base.py
|
|
||||||
│ │ ├── json_converter.py
|
|
||||||
│ │ ├── yaml_converter.py
|
|
||||||
│ │ ├── toml_converter.py
|
|
||||||
│ │ └── env_converter.py
|
|
||||||
│ ├── utils/
|
|
||||||
│ │ ├── __init__.py
|
|
||||||
│ │ ├── type_inference.py
|
|
||||||
│ │ ├── flatten.py
|
|
||||||
│ │ └── schema.py
|
|
||||||
│ └── validators/
|
|
||||||
│ ├── __init__.py
|
|
||||||
│ └── syntax.py
|
|
||||||
├── tests/
|
|
||||||
│ ├── __init__.py
|
|
||||||
│ ├── conftest.py
|
|
||||||
│ ├── test_cli.py
|
|
||||||
│ ├── test_converters/
|
|
||||||
│ ├── test_type_inference.py
|
|
||||||
│ ├── test_flatten.py
|
|
||||||
│ ├── test_schema.py
|
|
||||||
│ ├── test_validators.py
|
|
||||||
│ └── fixtures/
|
|
||||||
├── scripts/
|
|
||||||
│ └── completions.py
|
|
||||||
├── pyproject.toml
|
|
||||||
├── requirements.txt
|
|
||||||
└── README.md
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
1. Fork the repository
|
|
||||||
2. Create a feature branch
|
|
||||||
3. Make your changes
|
|
||||||
4. Run tests and linting
|
|
||||||
5. Submit a pull request
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT License - see LICENSE file for details.
|
MIT
|
||||||
|
|||||||
1
app/config_convert/cli.py
Normal file
1
app/config_convert/cli.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/config_convert/cli.py
|
||||||
1
app/config_convert/converters/base.py
Normal file
1
app/config_convert/converters/base.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/config_convert/converters/base.py
|
||||||
1
app/config_convert/converters/env_converter.py
Normal file
1
app/config_convert/converters/env_converter.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/config_convert/converters/env_converter.py
|
||||||
1
app/config_convert/converters/json_converter.py
Normal file
1
app/config_convert/converters/json_converter.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/config_convert/converters/json_converter.py
|
||||||
1
app/config_convert/converters/toml_converter.py
Normal file
1
app/config_convert/converters/toml_converter.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/config_convert/converters/toml_converter.py
|
||||||
1
app/config_convert/converters/yaml_converter.py
Normal file
1
app/config_convert/converters/yaml_converter.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/config_convert/converters/yaml_converter.py
|
||||||
1
app/config_convert/utils/flatten.py
Normal file
1
app/config_convert/utils/flatten.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/config_convert/utils/flatten.py
|
||||||
1
app/config_convert/validators/__init__.py
Normal file
1
app/config_convert/validators/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/config_convert/validators/__init__.py
|
||||||
1
app/config_convert/validators/syntax.py
Normal file
1
app/config_convert/validators/syntax.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/config_convert/validators/syntax.py
|
||||||
1
app/tests/conftest.py
Normal file
1
app/tests/conftest.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/conftest.py
|
||||||
1
app/tests/integration/test_full_workflow.py
Normal file
1
app/tests/integration/test_full_workflow.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/integration/test_full_workflow.py
|
||||||
1
app/tests/test_cli.py
Normal file
1
app/tests/test_cli.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/test_cli.py
|
||||||
1
app/tests/test_converters/test_json.py
Normal file
1
app/tests/test_converters/test_json.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/test_converters/test_json.py
|
||||||
1
app/tests/test_converters/test_toml.py
Normal file
1
app/tests/test_converters/test_toml.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/test_converters/test_toml.py
|
||||||
1
app/tests/test_extractors.py
Normal file
1
app/tests/test_extractors.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/test_extractors.py
|
||||||
1
app/tests/test_flatten.py
Normal file
1
app/tests/test_flatten.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/test_flatten.py
|
||||||
1
app/tests/test_formatters.py
Normal file
1
app/tests/test_formatters.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/test_formatters.py
|
||||||
1
app/tests/test_integration.py
Normal file
1
app/tests/test_integration.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/test_integration.py
|
||||||
1
app/tests/test_schema.py
Normal file
1
app/tests/test_schema.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/test_schema.py
|
||||||
1
app/tests/test_validators.py
Normal file
1
app/tests/test_validators.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/test_validators.py
|
||||||
1
app/tests/unit/test_cli.py
Normal file
1
app/tests/unit/test_cli.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/unit/test_cli.py
|
||||||
1
app/tests/unit/test_generators.py
Normal file
1
app/tests/unit/test_generators.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/unit/test_generators.py
|
||||||
1
app/tests/unit/test_parsers.py
Normal file
1
app/tests/unit/test_parsers.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/app/tests/unit/test_parsers.py
|
||||||
@@ -7,7 +7,6 @@ from typing import Optional
|
|||||||
import typer
|
import typer
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
from rich.syntax import Syntax
|
|
||||||
|
|
||||||
from config_convert import __version__
|
from config_convert import __version__
|
||||||
from config_convert.converters import (
|
from config_convert.converters import (
|
||||||
@@ -89,6 +88,7 @@ def convert(
|
|||||||
echo_error("Please specify --from when using --stdin")
|
echo_error("Please specify --from when using --stdin")
|
||||||
raise typer.Exit(2)
|
raise typer.Exit(2)
|
||||||
else:
|
else:
|
||||||
|
assert input_file is not None
|
||||||
fmt = from_format or get_format_from_path(input_file)
|
fmt = from_format or get_format_from_path(input_file)
|
||||||
data_str = Path(input_file).read_text(encoding="utf-8")
|
data_str = Path(input_file).read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
from config_convert.converters.base import Converter
|
from config_convert.converters.base import Converter
|
||||||
from config_convert.utils.type_inference import convert_value
|
from config_convert.utils.type_inference import convert_value
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class TOMLConverter(Converter):
|
|||||||
return tomllib.loads(data)
|
return tomllib.loads(data)
|
||||||
|
|
||||||
def dumps(self, data: Dict[str, Any], indent: Optional[int] = None) -> str:
|
def dumps(self, data: Dict[str, Any], indent: Optional[int] = None) -> str:
|
||||||
output = []
|
output: list = []
|
||||||
self._write_dict(data, output, indent=0)
|
self._write_dict(data, output, indent=0)
|
||||||
return "\n".join(output)
|
return "\n".join(output)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
"""Utilities for flattening and unflattening nested dictionaries."""
|
"""Utilities for flattening and unflattening nested dictionaries."""
|
||||||
|
|
||||||
import re
|
from typing import Any, Dict, List
|
||||||
from collections.abc import MutableMapping
|
|
||||||
from typing import Any, Dict, List, Tuple
|
|
||||||
|
|
||||||
|
|
||||||
def flatten_dict(data: Dict[str, Any], parent_key: str = "", sep: str = ".") -> Dict[str, Any]:
|
def flatten_dict(data: Dict[str, Any], parent_key: str = "", sep: str = ".") -> Dict[str, Any]:
|
||||||
@@ -104,7 +102,6 @@ def _split_key(key: str, sep: str) -> List[str]:
|
|||||||
def _set_nested(d: Any, parts: List[str], value: Any) -> None:
|
def _set_nested(d: Any, parts: List[str], value: Any) -> None:
|
||||||
"""Set a value in a nested structure, creating intermediate dicts or lists as needed."""
|
"""Set a value in a nested structure, creating intermediate dicts or lists as needed."""
|
||||||
current = d
|
current = d
|
||||||
path = [] # Track the path to enable modification at the right level
|
|
||||||
|
|
||||||
for i, part in enumerate(parts[:-1]):
|
for i, part in enumerate(parts[:-1]):
|
||||||
if isinstance(current, list):
|
if isinstance(current, list):
|
||||||
@@ -137,7 +134,7 @@ def _set_nested(d: Any, parts: List[str], value: Any) -> None:
|
|||||||
current[part] = {}
|
current[part] = {}
|
||||||
current = current[part]
|
current = current[part]
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Cannot traverse through non-dict/list object")
|
raise ValueError("Cannot traverse through non-dict/list object")
|
||||||
|
|
||||||
last_part = parts[-1]
|
last_part = parts[-1]
|
||||||
if isinstance(current, list):
|
if isinstance(current, list):
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""JSON Schema generation from parsed configurations."""
|
"""JSON Schema generation from parsed configurations."""
|
||||||
|
|
||||||
from typing import Any, Dict, List, Union
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
|
||||||
def generate_schema(data: Any) -> Dict[str, Any]:
|
def generate_schema(data: Any) -> Dict[str, Any]:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Smart type inference utilities for converting string values to Python types."""
|
"""Smart type inference utilities for converting string values to Python types."""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from typing import Any, Optional, Union
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
BOOLEAN_TRUE_VALUES = {"true", "yes", "on", "1"}
|
BOOLEAN_TRUE_VALUES = {"true", "yes", "on", "1"}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from typing import Optional, Tuple
|
||||||
from typing import Any, Optional, Tuple
|
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
@@ -104,9 +103,9 @@ def validate_env(data: str) -> Tuple[bool, Optional[ValidationError]]:
|
|||||||
if not stripped or stripped.startswith("#"):
|
if not stripped or stripped.startswith("#"):
|
||||||
continue
|
continue
|
||||||
if not line.strip() == line:
|
if not line.strip() == line:
|
||||||
return False, ValidationError(f"Invalid KEY=value format", line_num)
|
return False, ValidationError("Invalid KEY=value format", line_num)
|
||||||
if not ENV_LINE_PATTERN.match(line):
|
if not ENV_LINE_PATTERN.match(line):
|
||||||
return False, ValidationError(f"Invalid KEY=value format", line_num)
|
return False, ValidationError("Invalid KEY=value format", line_num)
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ dependencies = [
|
|||||||
dev = [
|
dev = [
|
||||||
"pytest>=7.4.0",
|
"pytest>=7.4.0",
|
||||||
"pytest-cov>=4.1.0",
|
"pytest-cov>=4.1.0",
|
||||||
|
"ruff>=0.4.0",
|
||||||
|
"mypy>=1.10.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
"""Tests package."""
|
"""Tests package."""
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|||||||
1
tests/fixtures/__init__.py
vendored
1
tests/fixtures/__init__.py
vendored
@@ -1 +1,2 @@
|
|||||||
"""Test fixtures package."""
|
"""Test fixtures package."""
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
|
||||||
from typer.testing import CliRunner
|
from typer.testing import CliRunner
|
||||||
|
|
||||||
from config_convert.cli import app
|
from config_convert.cli import app
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
"""Converters tests package."""
|
"""Converters tests package."""
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from config_convert.converters import ENVConverter
|
from config_convert.converters import ENVConverter
|
||||||
|
|
||||||
@@ -97,7 +96,6 @@ class TestENVConverter:
|
|||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
def test_dump_file(self, converter, temp_dir, sample_env):
|
def test_dump_file(self, converter, temp_dir, sample_env):
|
||||||
import shutil
|
|
||||||
path = tempfile.mktemp(suffix=".env")
|
path = tempfile.mktemp(suffix=".env")
|
||||||
try:
|
try:
|
||||||
data = converter.loads(sample_env)
|
data = converter.loads(sample_env)
|
||||||
@@ -105,7 +103,7 @@ class TestENVConverter:
|
|||||||
result = converter.load(path)
|
result = converter.load(path)
|
||||||
assert result == data
|
assert result == data
|
||||||
finally:
|
finally:
|
||||||
if Path(path).exists():
|
if os.path.exists(path):
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
def test_invalid_env_raises(self, converter):
|
def test_invalid_env_raises(self, converter):
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
"""Tests for JSON converter."""
|
"""Tests for JSON converter."""
|
||||||
|
|
||||||
import pytest
|
|
||||||
import json
|
import json
|
||||||
|
import pytest
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from config_convert.converters import JSONConverter
|
from config_convert.converters import JSONConverter
|
||||||
|
|
||||||
@@ -63,14 +62,13 @@ class TestJSONConverter:
|
|||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
def test_dump_file(self, converter, temp_dir, sample_json):
|
def test_dump_file(self, converter, temp_dir, sample_json):
|
||||||
import shutil
|
|
||||||
path = tempfile.mktemp(suffix=".json")
|
path = tempfile.mktemp(suffix=".json")
|
||||||
try:
|
try:
|
||||||
converter.dump(sample_json, path)
|
converter.dump(sample_json, path)
|
||||||
result = json.loads(Path(path).read_text())
|
result = json.loads(open(path).read())
|
||||||
assert result == sample_json
|
assert result == sample_json
|
||||||
finally:
|
finally:
|
||||||
if Path(path).exists():
|
if os.path.exists(path):
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
def test_invalid_json_raises(self, converter):
|
def test_invalid_json_raises(self, converter):
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from config_convert.converters import TOMLConverter
|
from config_convert.converters import TOMLConverter
|
||||||
@@ -63,7 +62,6 @@ class TestTOMLConverter:
|
|||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
def test_dump_file(self, converter, temp_dir, sample_toml):
|
def test_dump_file(self, converter, temp_dir, sample_toml):
|
||||||
import shutil
|
|
||||||
path = tempfile.mktemp(suffix=".toml")
|
path = tempfile.mktemp(suffix=".toml")
|
||||||
try:
|
try:
|
||||||
if sys.version_info >= (3, 11):
|
if sys.version_info >= (3, 11):
|
||||||
@@ -75,7 +73,7 @@ class TestTOMLConverter:
|
|||||||
result = converter.load(path)
|
result = converter.load(path)
|
||||||
assert result == data
|
assert result == data
|
||||||
finally:
|
finally:
|
||||||
if Path(path).exists():
|
if os.path.exists(path):
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
def test_invalid_toml_raises(self, converter):
|
def test_invalid_toml_raises(self, converter):
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
import yaml
|
||||||
|
|
||||||
from config_convert.converters import YAMLConverter
|
from config_convert.converters import YAMLConverter
|
||||||
|
|
||||||
@@ -56,7 +56,6 @@ class TestYAMLConverter:
|
|||||||
assert loaded == original
|
assert loaded == original
|
||||||
|
|
||||||
def test_load_file(self, converter, temp_file, sample_yaml):
|
def test_load_file(self, converter, temp_file, sample_yaml):
|
||||||
import yaml
|
|
||||||
gen = temp_file(sample_yaml, suffix=".yaml")
|
gen = temp_file(sample_yaml, suffix=".yaml")
|
||||||
path = next(gen)
|
path = next(gen)
|
||||||
try:
|
try:
|
||||||
@@ -66,8 +65,6 @@ class TestYAMLConverter:
|
|||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
def test_dump_file(self, converter, temp_dir, sample_yaml):
|
def test_dump_file(self, converter, temp_dir, sample_yaml):
|
||||||
import yaml
|
|
||||||
import shutil
|
|
||||||
path = tempfile.mktemp(suffix=".yaml")
|
path = tempfile.mktemp(suffix=".yaml")
|
||||||
try:
|
try:
|
||||||
data = yaml.safe_load(sample_yaml)
|
data = yaml.safe_load(sample_yaml)
|
||||||
@@ -75,7 +72,7 @@ class TestYAMLConverter:
|
|||||||
result = converter.load(path)
|
result = converter.load(path)
|
||||||
assert result == data
|
assert result == data
|
||||||
finally:
|
finally:
|
||||||
if Path(path).exists():
|
if os.path.exists(path):
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
def test_invalid_yaml_raises(self, converter):
|
def test_invalid_yaml_raises(self, converter):
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
"""Tests for JSON Schema generation."""
|
"""Tests for JSON Schema generation."""
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from config_convert.utils import generate_schema
|
from config_convert.utils import generate_schema
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
"""Tests for type inference module."""
|
"""Tests for type inference module."""
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from config_convert.utils import detect_type, convert_value, infer_types
|
from config_convert.utils import detect_type, convert_value, infer_types
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
"""Tests for syntax validation."""
|
"""Tests for syntax validation."""
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from config_convert.validators import validate
|
from config_convert.validators import validate
|
||||||
|
|
||||||
|
|
||||||
@@ -12,7 +10,7 @@ class TestValidateJSON:
|
|||||||
assert error is None
|
assert error is None
|
||||||
|
|
||||||
def test_valid_json_pretty(self):
|
def test_valid_json_pretty(self):
|
||||||
is_valid, error = validate('{\n "name": "test\n"}', "json")
|
is_valid, error = validate('{\n "name": "test"\n}', "json")
|
||||||
assert is_valid is True
|
assert is_valid is True
|
||||||
|
|
||||||
def test_invalid_json(self):
|
def test_invalid_json(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user