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

pipeline / pipenv: explicitly specify python version to workaround pipenv picking highest available #10

Merged
merged 10 commits into from
Jun 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/python-code-format.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: code format

on:
push:
pull_request:

jobs:
build:
runs-on: ubuntu-18.04
strategy:
matrix:
python-version:
- 3.8
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- run: pip install --upgrade pipenv>=2018.10.9
- run: pipenv install --python "$PYTHON_VERSION" --deploy --dev
env:
PYTHON_VERSION: ${{ matrix.python-version }}
- run: pipenv graph
- run: pipenv run black --check .
7 changes: 4 additions & 3 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,24 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- run: pip install --upgrade pipenv>=2018.10.9
- run: pipenv install --deploy --dev
- run: pipenv install --python "$PYTHON_VERSION" --deploy --dev
env:
PYTHON_VERSION: ${{ matrix.python-version }}
# ModuleNotFoundError: No module named 'importlib_metadata'
# https://github.com/WanzenBug/pylint-import-requirements/issues/17
- run: if python3 -c 'import sys; sys.exit(sys.version_info < (3, 8))'; then
pipenv graph;
pipenv install --dev importlib-metadata;
fi
- run: pipenv graph
- run: pipenv run pytest --cov=ical2vdir --cov-report=term-missing --cov-fail-under=100
- run: pipenv run pylint --load-plugins=pylint_import_requirements ical2vdir
# https://github.com/PyCQA/pylint/issues/352
# disable parse-error due to:
# > tests/resources/__init__.py:1:0: F0010: error while code parsing: Unable to load file tests/resources/__init__.py:
# > [Errno 2] No such file or directory: 'tests/resources/__init__.py' (parse-error)
- run: pipenv run pylint --disable=parse-error tests/*
- run: pipenv run mypy ical2vdir tests
- run: pipenv run pytest --cov=ical2vdir --cov-report=term-missing --cov-fail-under=100
- run: pipenv run black --check .
# >=1.9.0 to detect branch name
# https://github.com/coveralls-clients/coveralls-python/pull/207
# https://github.com/coverallsapp/github-action/issues/4#issuecomment-547036866
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Fixed
- python3.5:
- `TypeError` in `_write_event` when renaming temporary file
- `TypeError` in `_sync_event` when reading file
- tests: `TypeError` when converting to `pathlib.Path`
- tests: `AttributeError` due to unavailable `MagicMock.assert_called_once`

## [0.1.1] - 2020-02-06
### Fixed
Expand Down
11 changes: 10 additions & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@ name = "pypi"

[dev-packages]
ical2vdir = {editable = true,path = "."}
black = "==19.10b0"
# black requires python>=3.6
# https://github.com/psf/black/commit/e74117f172e29e8a980e2c9de929ad50d3769150#diff-2eeaed663bd0d25b7e608891384b7298R51
black = {version = "==19.10b0", markers="python_version >= '3.6'"}
mypy = "*"
# workaround https://github.com/pytest-dev/pytest/issues/3953
pathlib2 = {version = "*", markers="python_version < '3.6'"}
pylint = "*"
pylint-import-requirements = "*"
pytest = "*"
pytest-cov = "*"
# zipp v2.0.0 dropped support for python3.5
# https://github.com/jaraco/zipp/commit/05a3c52b4d41690e0471a2e283cffb500dc0329a
zipp = "<2"

[requires]
python_version = "3"

# Pipfile syntax: https://github.com/pypa/pipfile#pipfile
282 changes: 146 additions & 136 deletions Pipfile.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions ical2vdir/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ def _write_event(event: icalendar.cal.Event, path: pathlib.Path):
# https://tools.ietf.org/html/rfc5545#section-3.1
os.write(temp_fd, event.to_ical())
os.close(temp_fd)
os.rename(temp_path, path)
# python3.5 expects Union[bytes, str]
os.rename(temp_path, str(path))
finally:
if os.path.exists(temp_path):
os.unlink(temp_path)
Expand All @@ -112,7 +113,7 @@ def _sync_event(
_LOGGER.info("creating %s", output_path)
_write_event(event, output_path)
else:
with open(output_path, "rb") as current_file:
with output_path.open("rb") as current_file:
current_event = icalendar.Event.from_ical(current_file.read())
if _events_equal(event, current_event):
_LOGGER.debug("%s is up to date", output_path)
Expand Down
46 changes: 23 additions & 23 deletions tests/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ def test_entrypoint_help():


def test__main_create_all(
caplog, temp_dir_path: pathlib.Path, google_calendar_file: io.BufferedReader
caplog, tmp_path: pathlib.Path, google_calendar_file: io.BufferedReader
):
with unittest.mock.patch("sys.stdin", google_calendar_file):
with unittest.mock.patch("sys.argv", ["", "--output-dir", str(temp_dir_path)]):
with unittest.mock.patch("sys.argv", ["", "--output-dir", str(tmp_path)]):
with caplog.at_level(logging.INFO):
ical2vdir._main()
created_item_paths = sorted(temp_dir_path.iterdir())
created_item_paths = sorted(tmp_path.iterdir())
assert [p.name for p in created_item_paths] == [
"1234567890qwertyuiopasdfgh@google.com.ics",
"recurr1234567890qwertyuiop@google.com.20150908T090000+0200.ics",
Expand All @@ -58,12 +58,12 @@ def test__main_create_all(


def test__main_create_some(
caplog, temp_dir_path: pathlib.Path, google_calendar_file: io.BufferedReader
caplog, tmp_path: pathlib.Path, google_calendar_file: io.BufferedReader
):
with unittest.mock.patch("sys.stdin", google_calendar_file):
with unittest.mock.patch("sys.argv", ["", "--output-dir", str(temp_dir_path)]):
with unittest.mock.patch("sys.argv", ["", "--output-dir", str(tmp_path)]):
ical2vdir._main()
temp_dir_path.joinpath(
tmp_path.joinpath(
"recurr1234567890qwertyuiop@google.com.20150924T090000+0200.ics"
).unlink()
google_calendar_file.seek(0)
Expand All @@ -77,15 +77,15 @@ def test__main_create_some(


def test__main_update(
caplog, temp_dir_path: pathlib.Path, google_calendar_file: io.BufferedReader
caplog, tmp_path: pathlib.Path, google_calendar_file: io.BufferedReader
):
with unittest.mock.patch("sys.stdin", google_calendar_file):
with unittest.mock.patch("sys.argv", ["", "--output-dir", str(temp_dir_path)]):
with unittest.mock.patch("sys.argv", ["", "--output-dir", str(tmp_path)]):
ical2vdir._main()
temp_dir_path.joinpath(
tmp_path.joinpath(
"recurr1234567890qwertyuiop@google.com.20150924T090000+0200.ics"
).unlink()
updated_path = temp_dir_path.joinpath(
updated_path = tmp_path.joinpath(
"recurr1234567890qwertyuiop@google.com.20150908T090000+0200.ics"
)
updated_ical = updated_path.read_bytes().replace(b"20150908", b"20140703")
Expand All @@ -107,17 +107,17 @@ def test__main_update(


def test__main_update_silent(
caplog, temp_dir_path: pathlib.Path, google_calendar_file: io.BufferedReader
caplog, tmp_path: pathlib.Path, google_calendar_file: io.BufferedReader
):
with unittest.mock.patch("sys.stdin", google_calendar_file):
with unittest.mock.patch(
"sys.argv", ["", "--output-dir", str(temp_dir_path), "--silent"]
"sys.argv", ["", "--output-dir", str(tmp_path), "--silent"]
):
ical2vdir._main()
temp_dir_path.joinpath(
tmp_path.joinpath(
"recurr1234567890qwertyuiop@google.com.20150924T090000+0200.ics"
).unlink()
updated_path = temp_dir_path.joinpath(
updated_path = tmp_path.joinpath(
"recurr1234567890qwertyuiop@google.com.20150908T090000+0200.ics"
)
updated_ical = updated_path.read_bytes().replace(b"20150908", b"20140703")
Expand All @@ -130,17 +130,17 @@ def test__main_update_silent(


def test__main_update_verbose(
caplog, temp_dir_path: pathlib.Path, google_calendar_file: io.BufferedReader
caplog, tmp_path: pathlib.Path, google_calendar_file: io.BufferedReader
):
with unittest.mock.patch("sys.stdin", google_calendar_file):
with unittest.mock.patch(
"sys.argv", ["", "--output-dir", str(temp_dir_path), "--verbose"]
"sys.argv", ["", "--output-dir", str(tmp_path), "--verbose"]
):
ical2vdir._main()
temp_dir_path.joinpath(
tmp_path.joinpath(
"recurr1234567890qwertyuiop@google.com.20150924T090000+0200.ics"
).unlink()
updated_path = temp_dir_path.joinpath(
updated_path = tmp_path.joinpath(
"recurr1234567890qwertyuiop@google.com.20150908T090000+0200.ics"
)
updated_ical = updated_path.read_bytes().replace(b"20150908", b"20140703")
Expand All @@ -165,16 +165,16 @@ def test__main_update_verbose(


def test__main_delete(
caplog, temp_dir_path: pathlib.Path, google_calendar_file: io.BufferedReader
caplog, tmp_path: pathlib.Path, google_calendar_file: io.BufferedReader
):
temp_dir_path.joinpath("will-be-deleted.ics").touch()
tmp_path.joinpath("will-be-deleted.ics").touch()
with unittest.mock.patch("sys.stdin", google_calendar_file):
with unittest.mock.patch(
"sys.argv", ["", "--output-dir", str(temp_dir_path), "--delete"]
"sys.argv", ["", "--output-dir", str(tmp_path), "--delete"]
):
with caplog.at_level(logging.INFO):
ical2vdir._main()
assert len(list(temp_dir_path.iterdir())) == 3
assert not any(p.name == "will-be-deleted.ics" for p in temp_dir_path.iterdir())
assert len(list(tmp_path.iterdir())) == 3
assert not any(p.name == "will-be-deleted.ics" for p in tmp_path.iterdir())
assert caplog.records[-1].message.startswith("removing")
assert caplog.records[-1].message.endswith("will-be-deleted.ics")
5 changes: 0 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@
import pytest


@pytest.fixture
def temp_dir_path(tmpdir) -> pathlib.Path:
return pathlib.Path(tmpdir)


@pytest.fixture
def google_calendar_file() -> typing.Iterator[io.BufferedReader]:
with pathlib.Path(__file__).parent.joinpath(
Expand Down
34 changes: 17 additions & 17 deletions tests/vdir_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,16 @@ def _normalize_ical(ical: bytes) -> bytes:

# pylint: disable=protected-access

# tmp_path fixture: https://github.com/pytest-dev/pytest/blob/5.4.3/src/_pytest/tmpdir.py#L191

def test__write_event_cleanup(tmpdir):

def test__write_event_cleanup(tmp_path: pathlib.Path):
event = icalendar.cal.Event.from_ical(_SINGLE_EVENT_ICAL)
with unittest.mock.patch("os.unlink") as unlink_mock:
with pytest.raises(IsADirectoryError):
ical2vdir._write_event(event, pathlib.Path(tmpdir))
unlink_mock.assert_called_once()
ical2vdir._write_event(event, tmp_path)
# assert_called_once new in python3.6
assert unlink_mock.call_count == 1
unlink_args, _ = unlink_mock.call_args
os.unlink(unlink_args[0])

Expand Down Expand Up @@ -92,36 +95,33 @@ def test__event_vdir_filename(event_ical, expected_filename):


@pytest.mark.parametrize("event_ical", [_SINGLE_EVENT_ICAL])
def test__sync_event_create(tmpdir, event_ical):
temp_path = pathlib.Path(tmpdir)
def test__sync_event_create(tmp_path: pathlib.Path, event_ical):
event = icalendar.cal.Event.from_ical(event_ical)
ical2vdir._sync_event(event, temp_path)
(ics_path,) = temp_path.iterdir()
ical2vdir._sync_event(event, tmp_path)
(ics_path,) = tmp_path.iterdir()
assert ics_path.name == "1qa2ws3ed4rf5tg@google.com.ics"
assert ics_path.read_bytes() == _SINGLE_EVENT_ICAL


@pytest.mark.parametrize("event_ical", [_SINGLE_EVENT_ICAL])
def test__sync_event_update(tmpdir, event_ical):
temp_path = pathlib.Path(tmpdir)
def test__sync_event_update(tmp_path: pathlib.Path, event_ical):
event = icalendar.cal.Event.from_ical(event_ical)
ical2vdir._sync_event(event, temp_path)
ical2vdir._sync_event(event, tmp_path)
event["SUMMARY"] += " suffix"
ical2vdir._sync_event(event, temp_path)
(ics_path,) = temp_path.iterdir()
ical2vdir._sync_event(event, tmp_path)
(ics_path,) = tmp_path.iterdir()
assert ics_path.name == event["UID"] + ".ics"
assert ics_path.read_bytes() == _SINGLE_EVENT_ICAL.replace(
b"party", b"party suffix"
)


@pytest.mark.parametrize("event_ical", [_SINGLE_EVENT_ICAL])
def test__sync_event_unchanged(tmpdir, event_ical):
temp_path = pathlib.Path(tmpdir)
def test__sync_event_unchanged(tmp_path: pathlib.Path, event_ical):
event = icalendar.cal.Event.from_ical(event_ical)
ical2vdir._sync_event(event, temp_path)
(ics_path,) = temp_path.iterdir()
ical2vdir._sync_event(event, tmp_path)
(ics_path,) = tmp_path.iterdir()
old_stat = copy.deepcopy(ics_path.stat())
ical2vdir._sync_event(event, temp_path)
ical2vdir._sync_event(event, tmp_path)
assert ics_path.stat() == old_stat
assert ics_path.read_bytes() == _SINGLE_EVENT_ICAL