Skip to content

Commit

Permalink
Add type checking to Continuous Integration for Snowfakery (#740)
Browse files Browse the repository at this point in the history
* Type checking and some CI upgrades
  • Loading branch information
Paul Prescod committed Oct 7, 2022
1 parent 2f589a4 commit db6274a
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 89 deletions.
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ exclude_lines =
raise AssertionError
def __repr__
def __str__
if T.TYPE_CHECKING
if TYPE_CHECKING
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ jobs:
run: |
python setup.py install
snowbench snowfakery/tools/benchmark_1.yml --num-records 10_000 --num-records-tablename Account --number-of-processes 4
snowbench
snowbench | tee bench.txt
tail -n 7 bench.txt >> $GITHUB_STEP_SUMMARY
with_cci:
name: With CumulusCI
Expand Down Expand Up @@ -69,6 +70,9 @@ jobs:
diff-cover coverage.xml --fail-under 100 --compare-branch=_remote_main_ --diff-range-notation=.. --show-uncovered --markdown-report coverage.md
cat coverage.md >> $GITHUB_STEP_SUMMARY
- name: Type Check
run: pyright

faker_docs:
name: Faker Docs
runs-on: sfdc-ubuntu-latest
Expand Down
14 changes: 14 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[tool.pyright]
include = [
"snowfakery/utils/*",
"snowfakery/tools/*",
"snowfakery/__init__.py",
"snowfakery/__main__.py",
"snowfakery/cci_mapping_files/post_processes.py",
"snowfakery/data_gen_exceptions.py",
"snowfakery/salesforce.py",
"snowfakery/standard_plugins/SnowfakeryVersion.py",
"snowfakery/standard_plugins/__init__.py",
"snowfakery/standard_plugins/base64.py",
"snowfakery/object_rows.py",
]
3 changes: 2 additions & 1 deletion requirements/dev.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ diff-cover
mkdocs<1.3.0 # need to change Snowfakery monkey-patching before upgrade
mkdocs-exclude-search
pre-commit
pyright
pytest
pytest-cov
typeguard==2.10.0 # do not upgrade until #181 is fixed
typeguard
faker-microservice
tox
tox-gh-actions # needed for CI only
Expand Down
18 changes: 11 additions & 7 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ click==8.1.3
# -r requirements/prod.txt
# black
# mkdocs
coverage[toml]==6.4.4
coverage[toml]==6.5.0
# via
# -r requirements/dev.in
# coveralls
Expand Down Expand Up @@ -58,14 +58,14 @@ greenlet==1.1.3
# sqlalchemy
gvgen==1.0
# via -r requirements/prod.txt
identify==2.5.5
identify==2.5.6
# via pre-commit
idna==3.4
# via
# -r requirements/prod.txt
# requests
# yarl
importlib-metadata==4.12.0
importlib-metadata==5.0.0
# via
# markdown
# mkdocs
Expand Down Expand Up @@ -99,7 +99,9 @@ multidict==6.0.2
mypy-extensions==0.4.3
# via black
nodeenv==1.7.0
# via pre-commit
# via
# pre-commit
# pyright
packaging==21.3
# via
# mkdocs
Expand Down Expand Up @@ -128,14 +130,16 @@ pygments==2.13.0
# via diff-cover
pyparsing==3.0.9
# via packaging
pyright==1.1.274
# via -r requirements/dev.in
pyrsistent==0.18.1
# via jsonschema
pytest==7.1.3
# via
# -r requirements/dev.in
# pytest-cov
# pytest-vcr
pytest-cov==3.0.0
pytest-cov==4.0.0
# via -r requirements/dev.in
pytest-vcr==1.0.2
# via -r requirements/dev.in
Expand Down Expand Up @@ -184,7 +188,7 @@ tox==3.26.0
# tox-gh-actions
tox-gh-actions==2.10.0
# via -r requirements/dev.in
typeguard==2.10.0
typeguard==2.13.3
# via -r requirements/dev.in
typing-extensions==4.3.0
# via
Expand Down Expand Up @@ -216,5 +220,5 @@ zipp==3.8.1
# importlib-resources

# The following packages are considered to be unsafe in a requirements file:
setuptools==65.4.0
setuptools==65.4.1
# via nodeenv
24 changes: 24 additions & 0 deletions snowfakery/backports/typeguard_context_manager_hack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import contextlib
from typing import Callable, ContextManager, Iterator, TypeVar

T = TypeVar("T")

# until https://github.com/agronholm/typeguard/issues/115 is fully fixed
# or typeguard is removed, we need this hack.

# The fix is on typeguard/master and should be merged in Typeguard 3.0
#
# We need this feature to be moved from UNRELEASED to RELEASED:
#
# Changed the import hook to append @typechecked to the
# decorator list instead of inserting it as the first decorator
# (fixes type checking inconsistencies with mypy regarding at least
# @contextmanager, probably others too)


def contextmanager(
func: Callable[..., Iterator[T]]
) -> Callable[..., ContextManager[T]]:
result = contextlib.contextmanager(func)
result.__annotations__ = {**func.__annotations__, "return": ContextManager[T]}
return result
4 changes: 2 additions & 2 deletions snowfakery/fakedata/fake_data_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@ class FakeData:
def __init__(
self,
faker_providers: T.Sequence[object],
locale: str = None,
faker_context: PluginContext = None,
locale: T.Optional[str] = None,
faker_context: T.Optional[PluginContext] = None,
):
# access to persistent state
self.faker_context = faker_context
Expand Down
8 changes: 6 additions & 2 deletions snowfakery/object_rows.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from enum import Enum, auto
import typing as T

import yaml
import snowfakery # noqa
from .utils.yaml_utils import SnowfakeryDumper
from contextvars import ContextVar

IdManager = "snowfakery.data_generator_runtime.IdManager"
if T.TYPE_CHECKING:
from snowfakery.data_generator_runtime import IdManager
else:
IdManager = "snowfakery.data_generator_runtime.IdManager"
RowHistoryCV = ContextVar("RowHistory")


Expand Down Expand Up @@ -105,7 +109,7 @@ class NicknameSlot(ObjectReference):

_tablename: str
id_manager: IdManager
allocated_id: int = None
allocated_id: T.Union[T.Optional[int], SlotState] = None

def __init__(self, tablename: str, id_manager: IdManager):
self._tablename = tablename
Expand Down
69 changes: 2 additions & 67 deletions snowfakery/tools/storage_bench.py
Original file line number Diff line number Diff line change
@@ -1,69 +1,4 @@
""" Benchmarking tool for the RowHistory backing database"""

from snowfakery.row_history import RowHistory
from snowfakery.object_rows import RowHistoryCV
from time import time

COUNT = 200_000


def main(count):
rh: RowHistory = RowHistory()
RowHistoryCV.set(rh)
start = time()
for i in range(0, COUNT, 4):
rh.save_row(
"table",
None,
{
"id": i,
"blah": "blah",
},
)

rh.save_row(
"table",
"Nicknamed",
{
"id": i + 1,
"blah": "blah",
},
)

rh.save_row(
"table",
"Nicknamed2",
{
"id": i + 2,
"blah": "blah",
},
)

if i % 1000 == 0:
nickname = "Sparse"
else:
nickname = "Unused"

rh.save_row(
"table",
nickname,
{
"id": i + 3,
"blah": "blah",
},
)

end = time()
print("Saved", end - start)
start = time()
for i in range(0, COUNT):
rh.load_row("table", i)
assert rh.random_row_reference("table", "current-iteration", False).blah
assert rh.random_row_reference("Nicknamed", "current-iteration", False).blah
assert rh.random_row_reference("Nicknamed2", "current-iteration", False).blah
assert rh.random_row_reference("Sparse", "current-iteration", False).blah
end = time()
print("Loaded", end - start)


main(COUNT)
# Deleted after 2f589a4a44f7ff8e9a4650e978d24839d47f61d9
# Go back to that PR if you want that code back.
13 changes: 9 additions & 4 deletions snowfakery/utils/files.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import typing as T
from contextlib import contextmanager
from pathlib import Path

if T.TYPE_CHECKING:
from contextlib import contextmanager
else:
# until https://github.com/agronholm/typeguard/issues/115 is fixed in typeguard 3
from snowfakery.backports.typeguard_context_manager_hack import contextmanager

from click.utils import LazyFile

OpenFileLike = T.Union[T.TextIO, LazyFile]
Expand All @@ -11,7 +16,7 @@
@contextmanager
def open_file_like(
file_like: T.Optional[FileLike], mode, **kwargs
) -> T.ContextManager[T.Tuple[str, OpenFileLike]]:
) -> T.Generator[T.Tuple[T.Optional[Path], T.Optional[OpenFileLike]], None, None]:
"""Look at a file-like or path-like object and open it
Returns a) the path it was given OR a best-guess of the path and
Expand All @@ -30,7 +35,7 @@ def open_file_like(
yield file_like, f

elif hasattr(file_like, "name"):
yield file_like.name, file_like
yield Path(file_like.name), file_like # type: ignore

elif hasattr(file_like, "read"):
yield None, file_like
yield None, T.cast(OpenFileLike, file_like)
2 changes: 1 addition & 1 deletion snowfakery/utils/randomized_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


class UpdatableRandomRange:
def __init__(self, start: int, stop: int = None):
def __init__(self, start: int, stop: int):
assert stop > start
self.min = start
self._set_new_range_immediately(start, stop)
Expand Down
8 changes: 4 additions & 4 deletions snowfakery/utils/template_utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Sequence
import typing as T
import string
from snowfakery.fakedata.fake_data_generator import FakeData

Expand Down Expand Up @@ -46,9 +46,9 @@ class FakerTemplateLibrary:

def __init__(
self,
faker_providers: Sequence[object],
locale: str = None,
context: PluginContext = None,
faker_providers: T.Sequence[object],
locale: T.Optional[str] = None,
context: T.Optional[PluginContext] = None,
):
self.locale = locale
self.context = context
Expand Down

0 comments on commit db6274a

Please sign in to comment.