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 config #485

Open
5 tasks done
abrichr opened this issue Aug 28, 2023 · 2 comments · May be fixed by #516
Open
5 tasks done

Refactor config #485

abrichr opened this issue Aug 28, 2023 · 2 comments · May be fixed by #516
Assignees
Labels
enhancement New feature or request sweep Assigns Sweep to an issue or pull request.

Comments

@abrichr
Copy link
Contributor

abrichr commented Aug 28, 2023

Feature request

Currently we have:

_DEFAULTS = {
    "FOO": "bar",
    ...
}

This requires lots of linter pragmas. This task involves refactoring simply:

FOO = "bar"

This will involve considering loading variables from .env and saving defaults correctly (e.g. DEFAULTS = locals() ?)

Motivation

Too many linter pragmas when using config vars

Checklist
  • Extract openadapt/config.py26b994e
  • Create tests/test_config.py210457c
  • Check tests/test_config.py
  • Modify tests/test_config.py ! No changes made
  • Check tests/test_config.py

Flowchart

@abrichr abrichr added the enhancement New feature or request label Aug 28, 2023
@KrishPatel13 KrishPatel13 self-assigned this Sep 1, 2023
@abrichr abrichr added the sweep Assigns Sweep to an issue or pull request. label Nov 10, 2023
Copy link

sweep-ai bot commented Nov 10, 2023

Here's the PR! #516.

Sweep Basic Tier: I'm using GPT-4. You have 4 GPT-4 tickets left for the month and 2 for the day.

For more GPT-4 tickets, visit our payment portal. For a one week free trial, try Sweep Pro (unlimited GPT-4 tickets).

Actions (click)

  • ↻ Restart Sweep

Sandbox Execution ✓

Here are the sandbox execution logs prior to making any changes:

Sandbox logs for 5708256
pre-commit install 1/4 ✓
pre-commit installed at .git/hooks/pre-commit
trunk init 2/4 ✓
⡿ Downloading Trunk 1.17.2...
⡿ Downloading Trunk 1.17.2...
⢿ Downloading Trunk 1.17.2...
⣻ Downloading Trunk 1.17.2...
⣽ Downloading Trunk 1.17.2...
⣾ Downloading Trunk 1.17.2...
⣷ Downloading Trunk 1.17.2...
✔ Downloading Trunk 1.17.2... done
⡿ Verifying Trunk sha256...
✔ Verifying Trunk sha256... done
⡿ Unpacking Trunk...
✔ Unpacking Trunk... done


✔ 18 linters were enabled (.trunk/trunk.yaml)
  actionlint 1.6.26 (2 github-workflow files)
  bandit 1.7.5 (75 python files)
  black 23.9.1 (75 python files)
  checkov 3.0.32 (8 yaml files)
  flake8 6.1.0 (75 python files)
  git-diff-check (111 files)
  isort 5.12.0 (75 python files)
  markdownlint 0.37.0 (6 markdown files) (created .markdownlint.yaml)
  osv-scanner 1.4.3 (1 lockfile file)
  oxipng 9.0.0 (8 png files)
  prettier 3.0.3 (6 markdown, 8 yaml files)
  ruff 0.1.5 (75 python files) (created ruff.toml)
  shellcheck 0.9.0 (2 shell files) (created .shellcheckrc)
  shfmt 3.6.0 (2 shell files)
  taplo 0.8.1 (1 toml file)
  trivy 0.47.0 (8 yaml files)
  trufflehog 3.62.1 (111 files)
  yamllint 1.33.0 (8 yaml files) (created .yamllint.yaml)
Next Steps
 1. Read documentation
    Our documentation can be found at https://docs.trunk.io
 2. Get help and give feedback
    Join the Trunk community at https://slack.trunk.io
trunk fmt tests/test_config.py || exit 0 3/4 ✓
  NOTICES  
 tests/test_config.py  ignored empty file [black]
 Hint: use --force to check ignored files
Checked 1 file
✔ No issues
trunk check --fix --print-failures tests/test_config.py 4/4 ✓
  NOTICES  
 tests/test_config.py  ignored empty file [black]
 Hint: use --force to check ignored files
Checked 1 file
✔ No issues

Sandbox passed on the latest main, so sandbox checks will be enabled for this issue.

Install Sweep Configs: Pull Request

Step 1: 🔎 Searching

I found the following snippets in your repository. I will now analyze these snippets and come up with a plan.

Some code snippets I looked at (click to expand). If some file is missing from here, you can mention the path in the ticket description.

OpenAdapt/alembic.ini

Lines 5 to 70 in 5708256

script_location = alembic
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
# Uncomment the line below if you want the files to be prepended with date and time
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
# for all available tokens
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = .
# timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the python-dateutil library that can be
# installed by adding `alembic[tz]` to the pip requirements
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; This defaults
# to alembic/versions. When using multiple version
# directories, initial revisions must be specified with --version-path.
# The path separator used here should be the separator specified by "version_path_separator" below.
# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
# version path separator; As mentioned above, this is the character used to split
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
# Valid values for version_path_separator are:
#
# version_path_separator = :
# version_path_separator = ;
# version_path_separator = space
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = driver://user:pass@localhost/dbname
[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner, against the "black" entrypoint
# hooks = black
# black.type = console_scripts
# black.entrypoint = black
# black.options = -l 79 REVISION_SCRIPT_FILENAME

# 'US_SSN',
# 'MEDICAL_LICENSE'
],
"SCRUB_KEYS_HTML": [
"text",
"canonical_text",
"title",
"state",
"task_description",
"key_char",
"canonical_key_char",
"key_vk",
"children",
],
"PLOT_PERFORMANCE": True,
# VISUALIZATION CONFIGURATIONS
"VISUALIZE_DARK_MODE": False,
"VISUALIZE_RUN_NATIVELY": True,
"VISUALIZE_DENSE_TREES": True,
"VISUALIZE_ANIMATIONS": True,
"VISUALIZE_EXPAND_ALL": False, # not recommended for large trees
"VISUALIZE_MAX_TABLE_CHILDREN": 10,
# Calculate and save the difference between 2 neighboring screenshots
"SAVE_SCREENSHOT_DIFF": False,
"SPACY_MODEL_NAME": "en_core_web_trf",
"PRIVATE_AI_API_KEY": "<set your api key in .env>",
}
# each string in STOP_STRS should only contain strings
# that don't contain special characters
STOP_STRS = [
"oa.stop",
# TODO:
# "<ctrl>+c,<ctrl>+c,<ctrl>+c"
]
# each list in SPECIAL_CHAR_STOP_SEQUENCES should contain sequences
# containing special chars, separated by keys
SPECIAL_CHAR_STOP_SEQUENCES = [["ctrl", "ctrl", "ctrl"]]
# sequences that when typed, will stop the recording of ActionEvents in record.py
STOP_SEQUENCES = [
list(stop_str) for stop_str in STOP_STRS
] + SPECIAL_CHAR_STOP_SEQUENCES
ENV_FILE_PATH = ".env"
def getenv_fallback(var_name: str) -> str:
"""Get the value of an environment variable or fallback to the default value.
Args:
var_name (str): The name of the environment variable.
Returns:
The value of the environment variable or the fallback default value.
Raises:
ValueError: If the environment variable is not defined.
"""
rval = os.getenv(var_name) or _DEFAULTS.get(var_name)
if type(rval) is str and rval.lower() in (
"true",
"false",
"1",
"0",
):
rval = rval.lower() == "true" or rval == "1"
if type(rval) is str and rval.isnumeric():
rval = int(rval)
if rval is None:
raise ValueError(f"{var_name=} not defined")
return rval
def persist_env(var_name: str, val: str, env_file_path: str = ENV_FILE_PATH) -> None:
"""Persist an environment variable to a .env file.
Args:
var_name (str): The name of the environment variable.
val (str): The value of the environment variable.
env_file_path (str, optional): The path to the .env file (default: ".env").
"""
if not os.path.exists(env_file_path):
with open(env_file_path, "w") as f:
f.write(f"{var_name}={val}")
else:
# find and replace
with open(env_file_path, "r") as f:
lines = f.readlines()
for i, line in enumerate(lines):
if line.startswith(f"{var_name}="):
lines[i] = f"{var_name}={val}\n"
break
else:
# we didn't find the variable in the file, so append it
if lines[-1][-1] != "\n":
lines.append("\n")
lines.append(f"{var_name}={val}")
with open(env_file_path, "w") as f:
f.writelines(lines)
load_dotenv()
for key in _DEFAULTS:
val = getenv_fallback(key)
locals()[key] = val
ROOT_DIRPATH = pathlib.Path(__file__).parent.parent.resolve()
DB_FPATH = ROOT_DIRPATH / DB_FNAME # type: ignore # noqa
DB_URL = f"sqlite:///{DB_FPATH}"
DIRNAME_PERFORMANCE_PLOTS = "performance"
def obfuscate(val: str, pct_reveal: float = 0.1, char: str = "*") -> str:
"""Obfuscates a value by replacing a portion of characters.
Args:
val (str): The value to obfuscate.
pct_reveal (float, optional): Percentage of characters to reveal (default: 0.1).
char (str, optional): Obfuscation character (default: "*").
Returns:
str: Obfuscated value with a portion of characters replaced.
Raises:
AssertionError: If length of obfuscated value does not match original value.
"""
num_reveal = int(len(val) * pct_reveal)
num_obfuscate = len(val) - num_reveal
obfuscated = char * num_obfuscate
revealed = val[-num_reveal:]
rval = f"{obfuscated}{revealed}"
assert len(rval) == len(val), (val, rval)
return rval
_OBFUSCATE_KEY_PARTS = ("KEY", "PASSWORD", "TOKEN")
if multiprocessing.current_process().name == "MainProcess":

# How to contribute
We would love to implement your contributions to this project! We simply ask that you observe the following guidelines.
## Code Style
This project follows the [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html),
with the following modifications:
- imports are ordered first by group (in order of built-in, external, local), then lexicographically
- import groups are separated by newlines
For example:
```
# built-in
from functools import partial
from pprint import pformat
import json
# external
from loguru import logger
import numpy as np
# local
from openadapt import models
```
## Creating Issues
In order to effectively communicate any bugs or request new features, please select the appropriate form. If none of the options suit your needs, you can click on "Open a blank issue" located at the bottom.
## Testing
[GitHub Actions](https://github.com/MLDSAI/OpenAdapt/actions/new) are automatically run on each pull request to ensure consistent behavior and style. The Actions are composed of PyTest, [black](https://github.com/psf/black) and [flake8](https://flake8.pycqa.org/en/latest/user/index.html).
You can run these tests on your own computer by downloading the dependencies using `poetry` (see [here](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/README.md#install)) and then running `pytest` in the root directory.
## Pull Request Format
To speed up the review process, please use the provided pull request template and create a draft pull request to get initial feedback.
The pull request template includes areas to explain the changes, and a checklist with boxes for code style, testing, and documentation.


Step 2: ⌨️ Coding

  • Extract openadapt/config.py26b994e
Extract openadapt/config.py with contents:
• Extract the logic for loading variables from .env and saving defaults into separate functions.
• Replace the `_DEFAULTS` dictionary with direct variable definitions.
• Update the `getenv_fallback`, `persist_env`, and `obfuscate` functions to work with the new configuration management approach.
  • Create tests/test_config.py210457c
Create tests/test_config.py with contents:
• Unit tests for `getenv_fallback`, `persist_env`, and `obfuscate` functions, to be written in `tests/test_config.py`.
• Test `getenv_fallback` with both existing and non-existing environment variables. Check that it returns the correct values and raises a ValueError for non-existing variables without a default value.
• Test `persist_env` with different variable names and values. Check that it correctly updates the .env file.
• Test `obfuscate` with different input values, percentages, and characters. Check that it returns the correct obfuscated values.
  • Check tests/test_config.py
Sandbox logs for
trunk fmt tests/test_config.py || exit 0 1/2 ✓
 ✔ Formatted tests/test_config.py
Re-checking autofixed files...

 ✔ Formatted tests/test_config.py
Re-checking autofixed files...


Checked 1 file
✔ No issues
trunk check --fix --print-failures tests/test_config.py 2/2 ❌ (`1`)
  ISSUES  
tests/test_config.py:10:0
 10:0  low  Use of assert detected. The enclosed code will be removed when compiling to optimised byte       bandit/B101
            code.                                                                                                       
 11:0  low  Use of assert detected. The enclosed code will be removed when compiling to optimised byte       bandit/B101
            code.                                                                                                       
 23:0  low  Use of assert detected. The enclosed code will be removed when compiling to optimised byte       bandit/B101
            code.                                                                                                       
 27:0  low  Use of assert detected. The enclosed code will be removed when compiling to optimised byte       bandit/B101
            code.                                                                                                       
 28:0  low  Use of assert detected. The enclosed code will be removed when compiling to optimised byte       bandit/B101
            code.                                                                                                       
 29:0  low  Use of assert detected. The enclosed code will be removed when compiling to optimised byte       bandit/B101
            code.                                                                                                       
Checked 1 file
✖ 6 new issues
  • Modify tests/test_config.py ! No changes made
Modify tests/test_config.py with contents:
• Replace the assert statements in the `test_getenv_fallback` function with the `pytest.raises` function for the ValueError check and the `assert` function from the pytest module for the other checks.
• Replace the assert statement in the `test_persist_env` function with the `assert` function from the pytest module.
• Replace the assert statements in the `test_obfuscate` function with the `assert` function from the pytest module.
  • Check tests/test_config.py
Run `tests/test_config.py` through the sandbox.
  • Check tests/test_config.py
Sandbox logs for
trunk fmt tests/test_config.py || exit 0 1/2 ✓
 ✔ Formatted tests/test_config.py
Re-checking autofixed files...

 ✔ Formatted tests/test_config.py
Re-checking autofixed files...


Checked 1 file
✔ No issues
trunk check --fix --print-failures tests/test_config.py 2/2 ❌ (`1`)
  ISSUES  
tests/test_config.py:10:0
 10:0  low  Use of assert detected. The enclosed code will be removed when compiling to optimised byte       bandit/B101
            code.                                                                                                       
 11:0  low  Use of assert detected. The enclosed code will be removed when compiling to optimised byte       bandit/B101
            code.                                                                                                       
 23:0  low  Use of assert detected. The enclosed code will be removed when compiling to optimised byte       bandit/B101
            code.                                                                                                       
 27:0  low  Use of assert detected. The enclosed code will be removed when compiling to optimised byte       bandit/B101
            code.                                                                                                       
 28:0  low  Use of assert detected. The enclosed code will be removed when compiling to optimised byte       bandit/B101
            code.                                                                                                       
 29:0  low  Use of assert detected. The enclosed code will be removed when compiling to optimised byte       bandit/B101
            code.                                                                                                       
Checked 1 file
✖ 6 new issues

Step 3: 🔁 Code Review

I have finished reviewing the code for completeness. I did not find errors for sweep/refactor-config-management.


🎉 Latest improvements to Sweep:


💡 To recreate the pull request edit the issue title or description. To tweak the pull request, leave a comment on the pull request.
Join Our Discord

@abrichr
Copy link
Contributor Author

abrichr commented Nov 10, 2023

Unless there are compelling reasons otherwise, we likely want to go with Pydantic, as per #489 (comment), e.g:

from pydantic import BaseModel, Field
from typing import Optional

class Config(BaseModel):
    cache_dir_path: str = Field(default=".cache")
    cache_enabled: bool = Field(default=True)
    cache_verbosity: int = Field(default=0)
    db_echo: bool = Field(default=False)
    db_fname: str = Field(default="openadapt.db")

config = Config(_env_file='.env')

@sweep-ai sweep-ai bot linked a pull request Nov 10, 2023 that will close this issue
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request sweep Assigns Sweep to an issue or pull request.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants