Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
feat: initial repository setup
Browse files Browse the repository at this point in the history
  • Loading branch information
armand-sauzay committed Aug 21, 2024
1 parent 399b003 commit b41329c
Show file tree
Hide file tree
Showing 16 changed files with 450 additions and 93 deletions.
5 changes: 5 additions & 0 deletions .copier/.copier-answers.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
_commit: v1.2.1
_src_path: https://github.com/armand-sauzay/python-template.git
module_name: configer
module_version: 0.0.1
project_name: configer
1 change: 0 additions & 1 deletion .copier/.copier-answers.yaml.jinja

This file was deleted.

8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: v4.5.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.16.0
rev: v9.14.0
hooks:
- id: commitlint
stages: [commit-msg]
additional_dependencies: ["@commitlint/config-conventional"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.4
rev: v0.3.5
hooks:
- id: ruff
- id: ruff-format
Expand All @@ -22,7 +22,7 @@ repos:
- id: prettier
stages: [commit]
- repo: https://github.com/rhysd/actionlint
rev: v1.7.0
rev: v1.6.27
hooks:
- id: actionlint-docker
- repo: https://github.com/jumanjihouse/pre-commit-hooks
Expand Down
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.12.3
3.12.2
72 changes: 44 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,59 @@
# python-template
# configer

This is a template for Python-based repositories.
## Getting started

## Usage

The recommended way to use this repo is to

1. **Use this project as template**: and then run copier locally. Simply click on the "Use this template" button on the GitHub page to create a new repository based on this template.

2. **Run copier**: you can download [Copier](https://github.com/copier-org/copier) and then run the following command:

```bash
copier copy "https://github.com/armand-sauzay/python-template.git" <your_folder>
```

If you want to target a specific branch, you can use:
### Installation

```bash
copier copy --vcs-ref "<python-template-branch>" "https://github.com/armand-sauzay/python-template.git" <target-folder>
pip install configer
```

## Project files
### Usage

### .commitlintrc.json and .releaserc.json
There are three core concepts in `configer`:
- a config file: `config.yaml` or `config.json`
- a config schema: `schema.yaml` or `schema.json`
- a config object: an object of type `BaseConfig`

These files are used by `semantic-release` to determine the type of the next release. The `.commitlintrc.json` file is used to lint the commit messages. The `.releaserc.json` file is used to determine the version of the next release based on the commit messages. To lint commit messages, this project uses the default configuration of `@commitlint/config-conventional`.
#### 1. Create a config file

### .python-version file
Create a `config.json` file with the following content:

The `.python-version` file contains the python version used in this project. This project has been built with using `pyenv` as python version manager.
```json
{
"name": "John Doe",
"age": 30,
"is_student": true
}
```

### .pre-commit-config.yaml
#### 2. Create a schema file

Create a `schema.json` file with the following content:

```json
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
},
"is_student": {
"type": "boolean"
}
},
"required": ["name", "age", "is_student"]
}
```

This file is used by `pre-commit` to determine the hooks that will be run before each commit. The hooks are defined in the `hooks` section of the file. The hooks are run in the order they are defined in the file.
#### 3. Create a config object

### .github/workflows
```python
from configer import BaseConfig

This repository uses Github Actions for CI/CD. CI is composed of `Lint` with pre-commit and `Test` with pytest. Release is composed of `Lint`, `Test`, `Release` with semantic-release.
config = BaseConfig(config_file="config.json", schema_file="schema.json")
```

- Lint is done with [pre-commit](https://pre-commit.com/). To run lint locally, run `pre-commit run --all-files`.
- Test is done with [pytest](https://docs.pytest.org/en/8.0.x/). To run test locally, run `pytest`. Or poetry run `pytest` if you use poetry as package manager.
- Release is done with [semantic-release](https://github.com/semantic-release/semantic-release)
22 changes: 0 additions & 22 deletions README.md.jinja

This file was deleted.

1 change: 1 addition & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name": "John Doe", "age": 30, "custom": {"classname": "__main__.CustomClass", "params": {"name": "John Doe", "age": 30}}, "model": {"classname": "sklearn.linear_model.LogisticRegression", "params": {"C": 1.0, "max_iter": 100}}}
12 changes: 12 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
age: 25
custom:
classname: __main__.CustomClass
params:
age: 25
name: Jane Doe
model:
classname: sklearn.linear_model.LogisticRegression
params:
C: 1.0
max_iter: 100
name: Jane Doe
1 change: 1 addition & 0 deletions configer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .core import BaseConfig # noqa
65 changes: 65 additions & 0 deletions configer/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import importlib
import json
import yaml
from typing import Any, Dict, Type, TypeVar
from pydantic_settings import BaseSettings
from pydantic import BaseModel, field_serializer, model_validator

T = TypeVar("T", bound=BaseSettings)


def dynamic_instantiate(class_name: str, params: Dict[str, Any] = None) -> Any:
module_name, class_name = class_name.rsplit(".", 1)
module = importlib.import_module(module_name)
cls = getattr(module, class_name)
return cls(**params) if params else cls()


class DynamicConfig(BaseModel):
classname: str
params: Dict[str, Any] = None

def to_object(self) -> Any:
return dynamic_instantiate(self.classname, self.params)

@staticmethod
def from_object(obj: Any) -> "DynamicConfig":
class_name = f"{obj.__module__}.{obj.__class__.__name__}"
params = obj.get_params() if hasattr(obj, "get_params") else obj.__dict__
return DynamicConfig(classname=class_name, params=params)


class BaseConfig(BaseSettings):
@model_validator(mode="before")
@classmethod
def evaluate_dynamic_fields(cls, values: Dict[str, Any]) -> Dict[str, Any]:
for key, value in values.items():
if isinstance(value, dict) and "classname" in value:
dynamic_object = DynamicConfig(**value)
values[key] = dynamic_object.to_object()
return values

@field_serializer("*", mode="wrap")
def serialize_dynamic_fields(self, value, field) -> Any:
if hasattr(value, "__dict__"):
return DynamicConfig.from_object(value).dict()
return value

def model_dump_json(self, **kwargs) -> str:
data = self.model_dump(**kwargs)
return json.dumps(data)

@classmethod
def load_from_file(cls: Type[T], file_path: str) -> T:
if file_path.endswith(".json"):
with open(file_path, "r") as f:
config_data = json.load(f)
elif file_path.endswith(".yaml") or file_path.endswith(".yml"):
with open(file_path, "r") as f:
config_data = yaml.safe_load(f)
else:
raise ValueError(
"Unsupported file format. Please use a .json or .yaml file."
)

return cls(**config_data)
Loading

0 comments on commit b41329c

Please sign in to comment.