Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor generators and improve configuration handling #23

Merged
merged 3 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,5 @@ repos:
language: python
files: \.py$
pass_filenames: false
additional_dependencies:
- '.[email,regions]'
14 changes: 7 additions & 7 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ The settings for the CLI.

**Environment Prefix**: `PYDANTIC_SETTINGS_EXPORT__`

| Name | Type | Default | Description | Example |
|----------------------------------------------|----------------------|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| `PYDANTIC_SETTINGS_EXPORT__DEFAULT_SETTINGS` | `array` | `[]` | The default settings to use. The settings are applied in the order they are listed. | `["settings:settings"]`, `["app.config.settings:Settings","app.config.settings.dev:Settings"]` |
| `PYDANTIC_SETTINGS_EXPORT__ROOT_DIR` | `Path` | `"<project_dir>"` | The project directory. Used for relative paths in the configuration file and .env file. | `"<project_dir>"` |
| `PYDANTIC_SETTINGS_EXPORT__PROJECT_DIR` | `Path` | `"<project_dir>"` | The project directory. Used for relative paths in the configuration file and .env file. | `"<project_dir>"` |
| `PYDANTIC_SETTINGS_EXPORT__RESPECT_EXCLUDE` | `boolean` | `true` | Respect the exclude attribute in the fields. | `true` |
| `PYDANTIC_SETTINGS_EXPORT__ENV_FILE` | `Path` \| `NoneType` | `null` | The path to the `.env` file to load environment variables. Useful, then you have a Settings class/instance, which require values while running. | `null` |
| Name | Type | Default | Description | Example |
|----------------------------------------------|----------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| `PYDANTIC_SETTINGS_EXPORT__DEFAULT_SETTINGS` | `array` | `[]` | The default settings to use. The settings are applied in the order they are listed. | `["settings:settings"]`, `["app.config.settings:Settings","app.config.settings.dev:Settings"]` |
| `PYDANTIC_SETTINGS_EXPORT__ROOT_DIR` | `Path` | `"<project_dir>"` | The project directory. Used for relative paths in the configuration file and .env file. | `"<project_dir>"` |
| `PYDANTIC_SETTINGS_EXPORT__PROJECT_DIR` | `Path` | `"<project_dir>"` | The project directory. Used for relative paths in the configuration file and .env file. | `"<project_dir>"` |
| `PYDANTIC_SETTINGS_EXPORT__RESPECT_EXCLUDE` | `boolean` | `true` | Respect the exclude attribute in the fields. | `true` |
| `PYDANTIC_SETTINGS_EXPORT__ENV_FILE` | `Path` \| `NoneType` | `null` | he path to the .env file to load environment variables. Useful when you have a Settings class/instance, which requires values while running. | `null` |

### Relative Directory Settings

Expand Down
19 changes: 19 additions & 0 deletions docs/InjectedConfiguration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Example of injected configuration

After running the generation, all content which is between the `<!-- region:region_name -->` and `<!-- endregion:region_name -->` will be replaced with the generated configuration.

Here is an example of injected configuration with region name `config`:

<!-- region:config -->
Injected Configuration. Just a table.

| Name | Type | Default | Description | Example |
|------------------------------------------------------------|----------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| `PYDANTIC_SETTINGS_EXPORT__DEFAULT_SETTINGS` | `array` | `[]` | The default settings to use. The settings are applied in the order they are listed. | `["settings:settings"]`, `["app.config.settings:Settings","app.config.settings.dev:Settings"]` |
| `PYDANTIC_SETTINGS_EXPORT__ROOT_DIR` | `Path` | `"<project_dir>"` | The project directory. Used for relative paths in the configuration file and .env file. | `"<project_dir>"` |
| `PYDANTIC_SETTINGS_EXPORT__PROJECT_DIR` | `Path` | `"<project_dir>"` | The project directory. Used for relative paths in the configuration file and .env file. | `"<project_dir>"` |
| `PYDANTIC_SETTINGS_EXPORT__RESPECT_EXCLUDE` | `boolean` | `true` | Respect the exclude attribute in the fields. | `true` |
| `PYDANTIC_SETTINGS_EXPORT__ENV_FILE` | `Path` \| `NoneType` | `null` | he path to the .env file to load environment variables. Useful when you have a Settings class/instance, which requires values while running. | `null` |
| `PYDANTIC_SETTINGS_EXPORT__RELATIVE_TO__REPLACE_ABS_PATHS` | `boolean` | `true` | Replace absolute paths with relative path to project root. | `true` |
| `PYDANTIC_SETTINGS_EXPORT__RELATIVE_TO__ALIAS` | `string` | `"<project_dir>"` | The alias for the relative directory. | `"<project_dir>"` |
<!-- endregion:config -->
18 changes: 9 additions & 9 deletions docs/SimpleConfiguration.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
Simple Configuration. Just a table.

| Name | Type | Default | Description | Example |
|------------------------------------------------------------|----------------------|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| `PYDANTIC_SETTINGS_EXPORT__DEFAULT_SETTINGS` | `array` | `[]` | The default settings to use. The settings are applied in the order they are listed. | `["settings:settings"]`, `["app.config.settings:Settings","app.config.settings.dev:Settings"]` |
| `PYDANTIC_SETTINGS_EXPORT__ROOT_DIR` | `Path` | `"<project_dir>"` | The project directory. Used for relative paths in the configuration file and .env file. | `"<project_dir>"` |
| `PYDANTIC_SETTINGS_EXPORT__PROJECT_DIR` | `Path` | `"<project_dir>"` | The project directory. Used for relative paths in the configuration file and .env file. | `"<project_dir>"` |
| `PYDANTIC_SETTINGS_EXPORT__RESPECT_EXCLUDE` | `boolean` | `true` | Respect the exclude attribute in the fields. | `true` |
| `PYDANTIC_SETTINGS_EXPORT__ENV_FILE` | `Path` \| `NoneType` | `null` | The path to the `.env` file to load environment variables. Useful, then you have a Settings class/instance, which require values while running. | `null` |
| `PYDANTIC_SETTINGS_EXPORT__RELATIVE_TO__REPLACE_ABS_PATHS` | `boolean` | `true` | Replace absolute paths with relative path to project root. | `true` |
| `PYDANTIC_SETTINGS_EXPORT__RELATIVE_TO__ALIAS` | `string` | `"<project_dir>"` | The alias for the relative directory. | `"<project_dir>"` |
| Name | Type | Default | Description | Example |
|------------------------------------------------------------|----------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| `PYDANTIC_SETTINGS_EXPORT__DEFAULT_SETTINGS` | `array` | `[]` | The default settings to use. The settings are applied in the order they are listed. | `["settings:settings"]`, `["app.config.settings:Settings","app.config.settings.dev:Settings"]` |
| `PYDANTIC_SETTINGS_EXPORT__ROOT_DIR` | `Path` | `"<project_dir>"` | The project directory. Used for relative paths in the configuration file and .env file. | `"<project_dir>"` |
| `PYDANTIC_SETTINGS_EXPORT__PROJECT_DIR` | `Path` | `"<project_dir>"` | The project directory. Used for relative paths in the configuration file and .env file. | `"<project_dir>"` |
| `PYDANTIC_SETTINGS_EXPORT__RESPECT_EXCLUDE` | `boolean` | `true` | Respect the exclude attribute in the fields. | `true` |
| `PYDANTIC_SETTINGS_EXPORT__ENV_FILE` | `Path` \| `NoneType` | `null` | he path to the .env file to load environment variables. Useful when you have a Settings class/instance, which requires values while running. | `null` |
| `PYDANTIC_SETTINGS_EXPORT__RELATIVE_TO__REPLACE_ABS_PATHS` | `boolean` | `true` | Replace absolute paths with relative path to project root. | `true` |
| `PYDANTIC_SETTINGS_EXPORT__RELATIVE_TO__ALIAS` | `string` | `"<project_dir>"` | The alias for the relative directory. | `"<project_dir>"` |
4 changes: 2 additions & 2 deletions pydantic_settings_export/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ class PSECLISettings(PSESettings):
env_file: Path | None = Field(
None,
description=(
"The path to the `.env` file to load environment variables. "
"Useful, then you have a Settings class/instance, which require values while running."
"he path to the .env file to load environment variables. "
"Useful when you have a Settings class/instance, which requires values while running."
),
)

Expand Down
64 changes: 63 additions & 1 deletion pydantic_settings_export/generators/markdown.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import importlib.util
import warnings
from enum import StrEnum
from pathlib import Path
from typing import Literal, Self, TypedDict
from typing import Literal, Self, TypedDict, cast

from pydantic import ConfigDict, Field, model_validator

Expand Down Expand Up @@ -84,6 +85,18 @@ class MarkdownSettings(BaseGeneratorSettings):
examples=[True, False, "with-header"],
)

region: str | Literal[False] = Field(
False,
description=(
"The region to use for the table of the ALL settings (including sub-settings).\n"
"If a string is provided, the generator will insert content into that named region.\n"
"It replace all regions with the same to the same content.\n\n"
"NOTE: This option is only available if the `regions` extra is installed.\n"
'NOTE: For now, you cannot be able to control regions with the "region header option".'
),
examples=[False, "config"],
)

@model_validator(mode="after")
def validate_paths(self) -> Self:
"""Validate the paths."""
Expand All @@ -100,6 +113,15 @@ def validate_paths(self) -> Self:
self.paths = [p.absolute().resolve() for p in self.paths]
return self

@model_validator(mode="after")
def validate_region(self) -> Self:
"""Validate the region."""
if self.region:
spec = importlib.util.find_spec("text_region_parser")
if not spec or not spec.loader:
raise ValueError("The `region` option is only available if the `regions` extra is installed.")
return self

def __bool__(self) -> bool:
"""Check if the configuration file is set."""
return self.enabled and bool(self.paths)
Expand Down Expand Up @@ -203,3 +225,43 @@ def generate(self, *settings_infos: SettingsInfoModel) -> str:
else:
content += "\n\n".join(self.generate_single(s, 2).strip() for s in settings_infos)
return content.strip() + "\n"

def run(self, *settings_info: SettingsInfoModel) -> list[Path]:
"""Run the generator.

:param settings_info: The settings info to generate documentation for.
:return: The paths to generated documentation.
"""
# If the region is not set, run the generator as usual
region = self.generator_config.region
if not region:
return super().run(*settings_info)

import text_region_parser

result = f"\n{self.generate(*settings_info).strip()}\n"
file_paths = self.file_paths()

constructor = text_region_parser.RegionConstructor()

# Add the region with name from the config
# This region will be replaced with the generated content
constructor.add_parser(cast(str, region))(lambda _: result)

updated_files: list[Path] = []
for path in file_paths:
if not path.is_file():
raise FileNotFoundError(
f"The file {path} does not exist. "
f"Please, create this file before running the generator with the `region` option."
)

file_content = path.read_text()
new_content = constructor.parse_content(file_content)
if new_content == file_content:
# No need to update the file
continue
path.write_text(new_content)
updated_files.append(path)

return updated_files
17 changes: 16 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ dependencies = [
[project.scripts]
pydantic-settings-export = "pydantic_settings_export.cli:main"

[project.optional-dependencies]
email = [
"email-validator>=2.2.0",
]
regions = [
"text-region-parser>=0.1.1",
]

[dependency-groups]
dev = [
"mypy>=1.12.1",
Expand Down Expand Up @@ -159,7 +167,6 @@ exclude = [
disable_error_code = [
"call-arg",
]
enable_incomplete_feature = ["NewGenericSyntax"]

# /~https://github.com/jag-k/pydantic-settings-export
[tool.pydantic_settings_export]
Expand Down Expand Up @@ -196,3 +203,11 @@ file_prefix = "Simple Configuration. Just a table."
paths = [
"docs/SimpleConfiguration.md",
]

[[tool.pydantic_settings_export.generators.markdown]]
table_only = true
file_prefix = "Injected Configuration. Just a table."
region = "config"
paths = [
"docs/InjectedConfiguration.md",
]
Loading