Skip to content

Initial static typing refactor #5

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

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ jobs:
- name: Run tox with tox-gh-actions
uses: ymyzk/run-tox-gh-actions@main
with:
tox-version: "'>=4'"
tox-version: ">=4"
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"davidanson.vscode-markdownlint",
"littlefoxteam.vscode-python-test-adapter",
"nickmillerdev.pytest-fixtures",
"mgesbert.python-path"
"mgesbert.python-path",
"ms-python.mypy-type-checker"
]
}
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
"pyproject.toml",
"-v",
"-r"
]
],
"mypy-type-checker.path": ["mypy"]
}
3 changes: 2 additions & 1 deletion .vscode/waybar-check-gmail.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"davidanson.vscode-markdownlint",
"littlefoxteam.vscode-python-test-adapter",
"nickmillerdev.pytest-fixtures",
"mgesbert.python-path"
"mgesbert.python-path",
"ms-python.mypy-type-checker"
]
}
}
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Ready to contribute? Here's how to set up `waybar-check-gmail` for local develop
tox

To get [`isort`][6], [`flake8`][3], [`flake8-isort`][7], [`black`][5],
[`bandit`][9], [`pytest`][10], and [`tox`][4],
[`bandit`][9], [`pytest`][10], [`mypy`][15], and [`tox`][4],
just `pip install` them into your [`virtualenv`][1].

pip install -r requirements-dev.txt
Expand Down Expand Up @@ -178,6 +178,7 @@ Before you submit a pull request, check that it meets these guidelines:
[12]: https://github.com/pyenv/pyenv#installation
[13]: https://github.com/pyenv/pyenv/tree/master/plugins/python-build
[14]: https://coderwall.com/p/vj2jxg/select-install-python-versions-easily-with-fzf
[15]: https://mypy.readthedocs.io/en/stable/getting_started.html

<!-- TODO: Implement/document python-poetry -->
<!-- [11]: https://hackersandslackers.com/python-poetry-package-manager/ -->
Expand Down
30 changes: 28 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ legacy_tox_ini = """
[tox]
requires =
tox>=4
#env_list = linters, type, py{38,39,310,311}
env_list = linters, py{38,39,310,311}
env_list = linters, type, py{38,39,310,311}

[gh-actions]
python =
Expand Down Expand Up @@ -102,6 +101,14 @@ commands =
{[testenv:flake8]commands}
{[testenv:bandit]commands}

[testenv:type]
description = run type checking
basepython = python3.11
skip_install = true
deps =
{[testenv:mypy]deps}
commands =
{[testenv:mypy]commands}

[testenv:black]
description = run black code formatter
Expand Down Expand Up @@ -133,6 +140,17 @@ deps =
commands =
bandit -c "pyproject.toml" -v -r {toxinidir}/src


[testenv:mypy]
description = run mypy static type checker
skip_install = true
basepython = python3.11
deps =
mypy
commands =
mypy {posargs} {toxinidir}/src


"""

[tool.black]
Expand Down Expand Up @@ -171,6 +189,14 @@ exclude_dirs = [
"tests",
]

[tool.mypy]
ignore_missing_imports = true
follow_imports = "normal"
# ignore_missing_imports = true
show_column_numbers = true
# enable_error_code = "explicit-override"
pretty = true

[tool.pytest.ini_options]
addopts = ["--import-mode=importlib"]
pythonpath = [".", "src"]
Expand Down
16 changes: 16 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
autopep8==2.0.2
bandit==1.7.5
black==23.7.0
GitPython==3.1.32
flake8==6.0.0
flake8-isort==6.0.0
isort==5.12.0
mypy==1.3.0
mypy-extensions==1.0.0
pre-commit==2.20.0
pycodestyle==2.10.0
pyflakes==3.0.1
pytest==7.4.0
pytest-mock==3.10.0
pytest-runner==6.0.0
pytest-xdist==3.3.1
82 changes: 82 additions & 0 deletions src/waybar_check_gmail/config/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import configparser
from typing import TYPE_CHECKING

from util import die

if TYPE_CHECKING:
# from collections.abc import Iterable
from typing import Dict, Union

from typeshed import AnyPath

# from typing import List, Set, Dict, Tuple
else:
AnyPath = None


class Config:
_map: Dict[str, Dict[str, Union[str, bool, None]]] = {}

def __init__(self, fn: AnyPath):
self._map = {
"Auth": {
"Password": None,
"PasswordCommand": None,
"Username": None,
},
"CustomHeaders": {},
"General": {
"AuthMethod": "basic",
"Debug": False,
"DryRun": True,
"HTTPS": True,
"Hostname": None,
"InsecureSSL": False,
"Path": None,
"Verbose": False,
},
"OAuth2": {
"ClientID": None,
"ClientSecret": None,
"RedirectURI": "http://127.0.0.1",
"Scope": None,
},
}
self.verbose = self.get("General", "Verbose")

config = configparser.RawConfigParser()
# TODO: Why did calcurse-caldav set this function to str? MyPy + Flake8 say it's
# a syntax error
# config.optionxform = str
# TODO: Figure out precedence between --verbose arg parse and Config.verbose
# Maybe reimplement this as YAML instead
if self.verbose:
print("Loading configuration from " + fn + " ...")
try:
config.read_file(open(fn))
except FileNotFoundError:
die("Configuration file not found: {}".format(fn))

for sec in config.sections():
if sec not in self._map:
die("Unexpected config section: {}".format(sec))

if not self._map[sec]:
# Import section with custom key-value pairs.
self._map[sec] = dict(config.items(sec))
continue

# Import section with predefined keys.
for key, val in config.items(sec):
if key not in self._map[sec]:
die("Unexpected config key in section {}: {}".format(sec, key))
if isinstance(self._map[sec][key], bool):
self._map[sec][key] = config.getboolean(sec, key)
else:
self._map[sec][key] = val

def section(self, section: str) -> Dict[str, Union[str, bool, None]]:
return self._map[section]

def get(self, section: str, key: str) -> Union[str, bool, None]:
return self._map[section][key]
11 changes: 11 additions & 0 deletions src/waybar_check_gmail/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import sys

if sys.version_info[:2] >= (3, 8):
import importlib.metadata as importlib_metadata
else:
import importlib_metadata


__version__ = importlib_metadata.version(__name__.split(".", 1)[0])
__distribution__ = importlib_metadata.distribution(__name__.split(".", 1)[0])
PROGRAM_NAME: str = __distribution__.name
14 changes: 14 additions & 0 deletions tests/unit/test_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import sys

if sys.version_info[:2] >= (3, 8):
import importlib.metadata as importlib_metadata
else:
import importlib_metadata


def test_version():
from waybar_check_gmail import version

dist = importlib_metadata.distribution("waybar_check_gmail")
expected = dist.version
assert version.__version__ == expected