diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 74d074e..8f12794 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,6 +18,8 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install sass + run: npm install -g sass - name: Install dependencies run: | pip install .[dev] diff --git a/.hatch_build.py b/.hatch_build.py index 2b52525..d60bfd9 100644 --- a/.hatch_build.py +++ b/.hatch_build.py @@ -26,5 +26,5 @@ def load_about() -> dict[str, str]: with open( os.path.join(HERE, "tutordeck", "__about__.py"), "rt", encoding="utf-8" ) as f: - exec(f.read(), about) # pylint: disable=exec-used + exec(f.read(), about) return about diff --git a/Makefile b/Makefile index 01cf6a4..a0989ed 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,6 @@ .DEFAULT_GOAL := help .PHONY: docs SRC_DIRS = ./tutordeck -BLACK_OPTS = --exclude templates ${SRC_DIRS} runserver: ## Run a development server tutor deck runserver --dev @@ -13,22 +12,28 @@ scss-watch: ## Compile SCSS files to CSS and watch for changes $(MAKE) scss SASS_OPTS="--watch" # Warning: These checks are not necessarily run on every PR. -test: test-lint test-types test-format # Run some static checks. +test: test-lint test-types test-format test-pythonpackage # Run some static checks. test-format: ## Run code formatting tests - black --check --diff $(BLACK_OPTS) + ruff format --check --diff ${SRC_DIRS} test-lint: ## Run code linting tests - pylint --errors-only --enable=unused-import,unused-argument --ignore=templates --ignore=docs/_ext ${SRC_DIRS} + ruff check ${SRC_DIRS} test-types: ## Run type checks. mypy --exclude=templates --ignore-missing-imports --implicit-reexport --strict ${SRC_DIRS} -format: ## Format code automatically - black $(BLACK_OPTS) +build-pythonpackage: ## Build the "tutor-deck" python package for upload to pypi + python -m build --sdist -isort: ## Sort imports. This target is not mandatory because the output may be incompatible with black formatting. Provided for convenience purposes. - isort --skip=templates ${SRC_DIRS} +test-pythonpackage: build-pythonpackage ## Test that package can be uploaded to pypi + twine check dist/tutor_deck-$(shell make version).tar.gz + +format: ## Format code automatically + ruff format ${SRC_DIRS} + +fix-lint: ## Fix lint errors automatically + ruff check --fix ${SRC_DIRS} changelog-entry: ## Create a new changelog entry. scriv create @@ -36,6 +41,9 @@ changelog-entry: ## Create a new changelog entry. changelog: ## Collect changelog entries in the CHANGELOG.md file. scriv collect +version: ## Print the current tutor-deck version + @python -c 'import io, os; about = {}; exec(io.open(os.path.join("tutordeck", "__about__.py"), "rt", encoding="utf-8").read(), about); print(about["__version__"])' + ESCAPE =  help: ## Print this help @grep -E '^([a-zA-Z_-]+:.*?## .*|######* .+)$$' Makefile \ diff --git a/changelog.d/20250808_150533_muhammad.labeeb_release.md b/changelog.d/20250808_150533_muhammad.labeeb_release.md new file mode 100644 index 0000000..7d482d7 --- /dev/null +++ b/changelog.d/20250808_150533_muhammad.labeeb_release.md @@ -0,0 +1,2 @@ +- [Improvement] Migrate from pylint and black to ruff. (by @mlabeeb03) +- [Improvement] Test python package distribution build when running make test. (by @mlabeeb03) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index a9b2d3a..48d2c66 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,8 +42,7 @@ dev = [ "tutor[dev]>=20.0.0,<21.0.0", "types-aiofiles", "types-Markdown", - "pylint", - "black", + "ruff", ] [project.entry-points."tutor.plugin.v1"] @@ -75,3 +74,19 @@ path = ".hatch_build.py" [tool.hatch.build.targets.wheel] packages = ["tutordeck"] + +[tool.ruff] +exclude = ["templates", "docs/_ext"] + +[tool.ruff.lint] +# E: pycodestyle errors +# I: isort +# N: pep8-naming +select = ["E", "I", "N"] + +# F401: unused-import +# F841: unused-variable +# W292: missing-newline-at-end-of-file +extend-select = ["F401", "F841", "W292"] + +[tool.ruff.format] diff --git a/tutordeck/server/app.py b/tutordeck/server/app.py index f7d23af..7ffcedf 100644 --- a/tutordeck/server/app.py +++ b/tutordeck/server/app.py @@ -18,14 +18,13 @@ from quart import ( url_for, ) from quart.typing import ResponseTypes -from werkzeug.sansio.response import Response as BaseResponse from tutor.plugins.v1 import discover_package +from werkzeug.sansio.response import Response as BaseResponse from tutordeck.server.utils import current_page_plugins, pagination_context from . import constants, tutorclient - app = Quart( __name__, static_url_path="/static", @@ -372,8 +371,9 @@ async def process_config_update_request() -> None: cmd = ["config", "save"] for key, value in form.items(): if value.startswith("{{"): - # Templated values that start with {{ should be explicitely converted to string - # Otherwise there will be a parsing error because it might be considered a dictionary + # Templated values that start with {{ should be explicitely + # converted to string otherwise there will be a parsing + # error because it might be considered a dictionary value = f"'{value}'" cmd.extend(["--set", f"{key}={value}"]) tutorclient.CliPool.run_sequential(cmd) @@ -489,9 +489,9 @@ def update_plugins_requiring_launch( response: Response, add: t.Optional[str] = None, remove: t.Optional[str] = None ) -> None: """ - Store the list of plugins for which a recent set of changes require running "local launch". - - This list is stored as a "+"-separated string in a cookie. Note that flask will automatically put the content in quotes. + Store the list of plugins for which a recent set of changes require + running "local launch". This list is stored as a "+"-separated string + in a cookie. Note that flask will automatically put the content in quotes. """ # Note that comma, colon and semi-colon are not supported in cookie values separator = "+" diff --git a/tutordeck/server/tutorclient.py b/tutordeck/server/tutorclient.py index 32e4298..d286aea 100644 --- a/tutordeck/server/tutorclient.py +++ b/tutordeck/server/tutorclient.py @@ -50,7 +50,9 @@ class Project: @classmethod def get_user_config(cls) -> Config: """ - TODO load config dynamically from root anytime it is changed on disk? Maybe take the chance to clear sys.modules cache on reload? + TODO + Load config dynamically from root anytime it is changed on disk? + Maybe take the chance to clear sys.modules cache on reload? """ return tutor.config.get_user(cls.ROOT) @@ -131,8 +133,8 @@ class Cli: The first item is the log file path. Second item is the running command. This will handle gracefully file deletion. Note however that if the file is - truncated, all contents added to the beginning until the current position will be - missed. + truncated, all contents added to the beginning until the current position + will be missed. """ yield f"$ {self.command}\n" async with aiofiles.open(self.log_path, "rb") as f: