Skip to content

Commit

Permalink
Merge pull request #1 from kyoto7250/add_methods_for_find_project_root
Browse files Browse the repository at this point in the history
Add methods for find project root
  • Loading branch information
kyoto7250 authored May 13, 2023
2 parents 14975fe + 00e3a0f commit a203b3d
Show file tree
Hide file tree
Showing 8 changed files with 665 additions and 0 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/lint_and_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: lint_and_test
on: [pull_request]

jobs:
ci:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Run image
uses: abatilo/actions-poetry@v2
with:
poetry-version: "1.4.2"
- name: poetry install
run: poetry install
- name: black
run: poetry run black --check --verbose fastconfig/ tests/
- name: ruff
run: poetry run ruff check --exit-non-zero-on-fix fastconfig/ tests/
- name: isort
run: poetry run isort --check-only fastconfig/ tests/
- name: pytest
run: poetry run pytest
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,4 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.vscode/
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
style:
poetry run black fastconfig/ tests/
poetry run isort fastconfig/ tests/
poetry run ruff fastconfig/ tests/

test:
poetry run pytest --cov=fastconfig --cov-report=html
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,42 @@
# fastconfig
A lightweight way to find the project root and load config


## Abstract

This library provides two functionalities:
(The config function is under development)

* A function to search for files while traversing up to the project root.
- `fastconfig.find_project_root`
- `fastconfig.is_project_root`
- `fastconfig.search`
* A function to directly build a class from a configuration file.


## Install

```bash
pip install fastconfig
```

## Usage


```python
import fastconfig
import pathlib
from typing import Optional

path: Optional[pathlib.Path] = fastconfig.search("pyproject.toml")
if path is not None:
# TODO: read config
pass
```

## Motivation

In many projects, it is common to write configuration files, read them in code, and build Config classes. I created this library to enable these functions to be implemented by simply defining a class and specifying a file name (such as pyproject.toml).

## Contribution
If you have suggestions for features or improvements to the code, please feel free to create an issue or pull request.
104 changes: 104 additions & 0 deletions fastconfig/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import os
from pathlib import Path
from typing import List, Optional, Union

__version__ = "0.1.0"
PROJECT_ROOTS: List[str] = [".hg", ".git"]
DEPTH: int = 10


def is_project_root(path: Union[str, Path]) -> bool:
"""
Check the given path is the project root directory or not.
This method determines the project root by whether the version control tool directory exists.
Args:
path (Union[str, Path]): The path to check whether it is the project root or not
Returns:
bool: The result of the project root or not
"""
if isinstance(path, str):
path = Path(path)

if path.is_file():
path = path.parent

for candidate in PROJECT_ROOTS:
if path.joinpath(candidate).is_dir():
return True
return False


def find_project_root(path: Optional[Union[str, Path]] = None) -> Optional[Path]:
"""
Return if the project root is found, or None if not
Args:
path (Optional[Union[str, Path]]): A path string or Path object to start searching, If nothing is passed, start in the current directory
Returns:
Optional[Path]: the project root path, or None if the project root is not found
"""
if path is None:
path = os.getcwd()
cnt: int = 0

candidate: Path = Path(path) if isinstance(path, str) else path
if is_project_root(candidate):
return candidate

while cnt < DEPTH:
candidate = candidate.parent
if is_project_root(candidate):
return candidate

if candidate.parent == candidate:
return None

cnt += 1
return None


def search(
target: Union[str, Path],
path: Optional[Union[str, Path]] = None,
end_up_the_project_root: bool = True,
) -> Optional[Path]:
"""
Recursively searches for files with the name of the target and returns the result
Args:
target (Union[str, Path]): Search target filename, and directory names are ignored
path (Optional[Union[str, Path]]): A path string or Path object to start searching, If nothing is passed, start in the current directory
end_up_the_project_root (bool): Whether or not to search the directory where the version control tool exists
Returns:
Optional[Path]: a path of the target file, or None if the target file is not found
"""
if isinstance(target, str):
target = Path(target)

target_name: str = target.name
if path is None:
path = os.getcwd()

cnt: int = 0
candidate: Path = Path(os.path.join(path, target_name))
if candidate.exists():
return candidate
if is_project_root(candidate):
return None

while cnt < DEPTH:
candidate = candidate.parent.parent.joinpath(target_name)
if candidate.exists():
return candidate

if candidate.parent.parent.joinpath(target_name) == candidate or (
end_up_the_project_root and is_project_root(candidate)
):
return None

cnt += 1
return None
Loading

0 comments on commit a203b3d

Please sign in to comment.