diff --git a/.dockerignore b/.dockerignore index 0b1e1e7ef2..ab95e851c5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,27 +1,21 @@ -**/__pycache__ -**/.venv -**/.classpath -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/bin -**/charts -**/docker-compose* -**/compose* -**/Dockerfile* -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -LICENSE -README.md +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +env +pip-log.txt +pip-delete-this-directory.txt +.tox +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +*.log +.git +service.json +.github +.circleci +gha-creds-*.json diff --git a/.graphqlconfig b/.graphqlconfig deleted file mode 100644 index f0463e58c4..0000000000 --- a/.graphqlconfig +++ /dev/null @@ -1,4 +0,0 @@ -{ - "schema": "./graphql_api/types/me/me.graphql", - "documents": "**/*.graphql" -} diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 48392afa10..0000000000 --- a/Dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -# For more information, please refer to https://aka.ms/vscode-docker-python -FROM python:3-slim - -EXPOSE 8000 - -# Keeps Python from generating .pyc files in the container -ENV PYTHONDONTWRITEBYTECODE=1 - -# Turns off buffering for easier container logging -ENV PYTHONUNBUFFERED=1 - -# Install pip requirements -COPY requirements.txt . -RUN python -m pip install -r requirements.txt - -WORKDIR /app -COPY . /app - -# Creates a non-root user with an explicit UID and adds permission to access the /app folder -# For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers -RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app -USER appuser - -# During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug -CMD ["gunicorn", "--bind", "0.0.0.0:8000", "codecov.wsgi"] diff --git a/Makefile b/Makefile index c5bbdfde3c..e0b925da66 100644 --- a/Makefile +++ b/Makefile @@ -57,25 +57,6 @@ lint.check: echo "Formatting..." ruff format --check -# lint.branch lints only those files changed between this branch and main -# this command can be removed once we implement a pre-commit runner -# run it from a virtual env on your local machine - `cd ; python -m venv ./venv; source venv/bin/activate; pip-compile requirements.in; make lint.branch` -lint.branch: - make lint.install - echo "Linting files touched in this branch..." - changed_files=$$(git diff --name-only origin/main -- '*.py'); \ - if [ -n "$$changed_files" ]; then \ - ruff check $$changed_files --fix; \ - else \ - echo "No Python files to lint."; \ - fi - echo "Formatting files touched in this branch..." - if [ -n "$$changed_files" ]; then \ - ruff format $$changed_files --fix; \ - else \ - echo "No Python files to format."; \ - fi - build.requirements: # if docker pull succeeds, we have already build this version of # requirements.txt. Otherwise, build and push a version tagged diff --git a/graphql_api/context.py b/graphql_api/context.py deleted file mode 100644 index 6454767935..0000000000 --- a/graphql_api/context.py +++ /dev/null @@ -1,24 +0,0 @@ -from typing import Generic, TypeVar - -from graphql.type.definition import GraphQLResolveInfo - -T = TypeVar("T") - - -class TypedResolverInfo(GraphQLResolveInfo, Generic[T]): - """ - TypedResolverInfo adds type safety to the `context` field of Ariadne's - GraphQLResolveInfo by letting us declare the expected structure. - - Example usage: - - class CoverageAnalyticsContext(Protocol): - repository: Optional[Repository] - - def resolve_percent_covered( - coverage_analytics: CoverageAnalytics, info: TypedResolverInfo[CoverageAnalyticsContext] - ) -> Optional[float]: - repository = info.context.repository # we are able to use IDE autocomplete here - return repository.recent_coverage if repository else None - """ - context: T diff --git a/graphql_api/graphql.config.yml b/graphql_api/graphql.config.yml deleted file mode 100644 index 1ccd330f87..0000000000 --- a/graphql_api/graphql.config.yml +++ /dev/null @@ -1,2 +0,0 @@ -schema: schema.graphql -documents: '**/*.graphql' diff --git a/graphql_api/tests/test_coverage_analytics.py b/graphql_api/tests/test_coverage_analytics.py index 4970a43028..78101ec620 100644 --- a/graphql_api/tests/test_coverage_analytics.py +++ b/graphql_api/tests/test_coverage_analytics.py @@ -1,91 +1,99 @@ import datetime +from typing import Any, Dict, Optional from django.test import TransactionTestCase from django.utils import timezone +from freezegun import freeze_time from codecov_auth.tests.factories import OwnerFactory -from core.models import Repository +from core.models import Commit, Repository from core.tests.factories import ( CommitFactory, RepositoryFactory, ) +from graphql_api.tests.helper import GraphQLTestHelper +from graphql_api.types.coverage_analytics.coverage_analytics import ( + CoverageAnalyticsProps, + resolve_coverage_analytics_result_type, +) +from graphql_api.types.errors.errors import NotFoundError -from .helper import GraphQLTestHelper - -query_coverage_analytics_base_fields = """ -query CoverageAnalytics($owner:String!, $repo: String!) { - owner(username:$owner) { - repository(name: $repo) { - __typename - ... on Repository { - name - coverageAnalytics { - %s - } - } - ... on ResolverError { - message - } - } - } -} -""" - -default_coverage_analytics_base_fields = """ - percentCovered - commitSha - hits - misses - lines -""" - - -class TestFetchCoverageAnalyticsBaseFields(GraphQLTestHelper, TransactionTestCase): - def fetch_coverage_analytics( - self, repo_name, fields=None - ): - query = query_coverage_analytics_base_fields % (fields or default_coverage_analytics_base_fields) - variables = {"owner": "codecov-user", "repo": repo_name} - return self.gql_request(query=query, owner=self.owner, variables=variables) - - def setUp(self): + +class TestFetchCoverageAnalytics(GraphQLTestHelper, TransactionTestCase): + # SETUP + def setUp(self) -> None: self.owner = OwnerFactory(username="codecov-user") self.yaml = {"test": "test"} - def test_coverage_analytics_base_fields(self): - # Create repo, commit, and coverage data - repo = RepositoryFactory( + # some field resolvers require access to postgres or timeseries db + databases = {'default', 'timeseries'} + + # HELPERS + def run_gql_query(self, query: str, variables: Dict[str, Any]) -> Dict[str, Any]: + owner = self.owner + # Use the gql_request method from the parent class (GraphQLTestHelper) + return super().gql_request(query=query, owner=owner, variables=variables) + + def create_repository(self, name: str) -> Repository: + return RepositoryFactory( author=self.owner, active=True, private=True, - name="b", + name=name, yaml=self.yaml, language="erlang", languages=[], ) + + @staticmethod + def create_commit(repository: Repository, coverage_totals: Dict[str, int], + timestamp: Optional[datetime.datetime] = None) -> Commit: + if timestamp is None: + timestamp = timezone.now() + return CommitFactory(repository=repository, totals=coverage_totals, timestamp=timestamp) + + # TESTS + def test_coverage_analytics_base_fields(self) -> None: + """Test case where to fetch coverage analytics fields""" + + # Create repo, commit, and coverage data + repo = self.create_repository("myname") hour_ago = timezone.make_aware(datetime.datetime(2020, 12, 31, 23, 0)) - coverage_commit = CommitFactory( - repository=repo, - totals={"c": 75, "h": 30, "m": 10, "n": 40}, - timestamp=hour_ago, + coverage_commit = self.create_commit( + repository=repo, coverage_totals={"c": 75, "h": 30, "m": 10, "n": 40}, timestamp=hour_ago ) - CommitFactory(repository=repo, totals={"c": 85}) - - # Update the timestamp and save to db + self.create_commit(repository=repo, coverage_totals={"c": 85}) repo.updatestamp = timezone.now() repo.save() - self.assertTrue( - repo.pk, "Repository should be saved and have a primary key." - ) - - # Query the db using the repository model - repo_from_db = Repository.objects.get(pk=repo.pk) - self.assertIsNotNone(repo_from_db.updatestamp) + self.assertTrue(repo.pk, "Repository should be saved and have a primary key.") - # Fetch the coverage analytics data - coverage_analytics_data = self.fetch_coverage_analytics(repo.name) + # Set up the GraphQL query and run + query = """ + query CoverageAnalytics($owner: String!, $repo: String!) { + owner(username: $owner) { + repository(name: $repo) { + __typename + ... on Repository { + name + coverageAnalytics { + percentCovered + commitSha + hits + misses + lines + } + } + ... on ResolverError { + message + } + } + } + } + """ + variables = {"owner": self.owner.username, "repo": repo.name} + resp = self.run_gql_query(query=query, variables=variables) - # Define the expected response + # Assert the response matches expected_response = { "__typename": "Repository", "name": repo.name, @@ -97,48 +105,188 @@ def test_coverage_analytics_base_fields(self): "lines": 40, }, } + assert resp["owner"]["repository"] == expected_response - # Compare the actual data with the expected data - assert coverage_analytics_data["owner"]["repository"] == expected_response + def test_coverage_analytics_base_fields_partial(self) -> None: + """Test case where the query only expects one of the fields in CoverageAnalytics""" - def test_coverage_analytics_base_fields_partial(self): - repo = RepositoryFactory( - author=self.owner, - active=True, - private=True, - name="b", - yaml=self.yaml, - language="erlang", - languages=[], - ) + # Create repo and a single commit + repo = self.create_repository("testtest") hour_ago = timezone.make_aware(datetime.datetime(2020, 12, 31, 23, 0)) - CommitFactory(repository=repo, totals={"c": 75, "h": 30, "m": 10, "n": 40}, timestamp=hour_ago) + self.create_commit(repository=repo, coverage_totals={"c": 75, "h": 30, "m": 10, "n": 40}, timestamp=hour_ago) repo.updatestamp = timezone.now() repo.save() + self.assertTrue(repo.pk, "Repository should be saved and have a primary key.") - fields = "percentCovered" - coverage_data = self.fetch_coverage_analytics(repo.name, fields=fields) - print(coverage_data) - assert coverage_data["owner"]["repository"]["coverageAnalytics"]["percentCovered"] == 75 + # Set up the GraphQL query and run - requests only the `percentCovered` field + query = """ + query CoverageAnalytics($owner: String!, $repo: String!) { + owner(username: $owner) { + repository(name: $repo) { + __typename + ... on Repository { + coverageAnalytics { + percentCovered + } + } + ... on ResolverError { + message + } + } + } + } + """ + variables = {"owner": "codecov-user", "repo": repo.name} + resp = self.run_gql_query(query=query, variables=variables) - def test_coverage_analytics_no_commit(self): + # Assert the response matches the expected percentCovered value + assert resp["owner"]["repository"]["coverageAnalytics"]["percentCovered"] == 75 + + def test_coverage_analytics_no_commit(self) -> None: """Test case where no commits exist for coverage data""" - repo = RepositoryFactory( - author=self.owner, - active=True, - private=True, - name="empty-repo", - yaml=self.yaml, - language="erlang", - languages=[], - ) + + # Create repo without commits + repo = self.create_repository("empty-repo") + repo.updatestamp = timezone.now() repo.save() + self.assertTrue(repo.pk, "Repository should be saved and have a primary key.") + + # Set up the GraphQL query and run + query = """ + query CoverageAnalytics($owner: String!, $repo: String!) { + owner(username: $owner) { + repository(name: $repo) { + __typename + ... on Repository { + coverageAnalytics { + percentCovered + commitSha + hits + misses + lines + } + } + ... on ResolverError { + message + } + } + } + } + """ + variables = {"owner": "codecov-user", "repo": repo.name} + resp = self.run_gql_query(query=query, variables=variables) - coverage_data = self.fetch_coverage_analytics(repo.name) - assert coverage_data["owner"]["repository"]["coverageAnalytics"] == { + # Assert the response matches the expected structure with `None` values + assert resp["owner"]["repository"]["coverageAnalytics"] == { "percentCovered": None, "commitSha": None, "hits": None, "misses": None, "lines": None, } + + def test_coverage_analytics_resolves_to_error(self) -> None: + """Test case where the query resolves to an error (e.g., repository not found)""" + + # Set up and run the query to simulate a repository that doesn't exist + query = """ + query CoverageAnalytics($owner: String!, $repo: String!) { + owner(username: $owner) { + repository(name: $repo) { + __typename + ... on Repository { # Use an inline fragment for the Repository type + coverageAnalytics { + percentCovered + } + } + ... on ResolverError { + message + } + } + } + } + """ + variables = {"owner": "codecov-user", "repo": "non-existent-repo"} + coverage_data = self.run_gql_query(query=query, variables=variables) + + # Assert that the response resolves to an error + assert coverage_data["owner"]["repository"]["__typename"] == "NotFoundError" + assert coverage_data["owner"]["repository"]["message"] == "Not found" + + @freeze_time("2022-01-02") + def test_coverage_analytics_with_interval(self): + """Test with interval argument to fetch coverage data in a specific time range""" + + # Create data to populate the timeseries graph + repo = self.create_repository("test-repo") + one_day_ago = timezone.make_aware(datetime.datetime(2022, 1, 1, 0, 0)) + self.create_commit(repository=repo, coverage_totals={"c": 65, "h": 20, "m": 5, "n": 25}, timestamp=one_day_ago) + + two_days_ago = timezone.make_aware(datetime.datetime(2022, 1, 2, 0, 0)) + self.create_commit(repository=repo, coverage_totals={"c": 75, "h": 30, "m": 10, "n": 40}, timestamp=two_days_ago) + + repo.updatestamp = timezone.now() + repo.save() + self.assertTrue(repo.pk, "Repository should be saved and have a primary key.") + + # Set up GraphQL query and run + query = """ + query CoverageAnalytics($owner:String!, $repo: String!, $interval: MeasurementInterval!) { + owner(username:$owner) { + repository(name: $repo) { + __typename + ... on Repository { + name + coverageAnalytics { + measurements(interval: $interval) { + timestamp + avg + min + max + } + } + } + ... on ResolverError { + message + } + } + } + } + """ + variables = { + "owner": "codecov-user", + "repo": repo.name, + "interval": "INTERVAL_1_DAY" + } + resp = self.run_gql_query(query=query, variables=variables) + + expected_response = { + "__typename": "Repository", + "name": repo.name, + "coverageAnalytics": { + "measurements": [ + {'avg': 65.0, 'max': 65.0, 'min': 65.0, 'timestamp': '2022-01-01T00:00:00+00:00'}, + {'avg': 75.0, 'max': 75.0, 'min': 75.0, 'timestamp': '2022-01-02T00:00:00+00:00'} + ] + }, + } + + assert resp["owner"]["repository"] == expected_response + + def test_resolve_coverage_analytics_result_type_for_coverage_analytics_props(self) -> None: + """Test that the resolver returns 'CoverageAnalyticsProps' when passed a CoverageAnalyticsProps object""" + repo = self.create_repository("test") + coverage_analytics_props = CoverageAnalyticsProps(repository=repo) + result_type = resolve_coverage_analytics_result_type(coverage_analytics_props) + self.assertEqual(result_type, "CoverageAnalyticsProps") + + def test_resolve_coverage_analytics_result_type_for_not_found_error(self) -> None: + """Test that the resolver returns 'NotFoundError' when passed a NotFoundError object""" + result_type = resolve_coverage_analytics_result_type(NotFoundError()) + self.assertEqual(result_type, "NotFoundError") + + def test_resolve_coverage_analytics_result_type_for_unexpected_type(self) -> None: + """Test that the resolver returns None when passed an object of an unexpected type""" + unexpected_object = "unexpected_string" + result_type = resolve_coverage_analytics_result_type(unexpected_object) + self.assertIsNone(result_type) diff --git a/graphql_api/tests/test_coverage_analytics_measurements.py b/graphql_api/tests/test_coverage_analytics_measurements.py index 83adb499ce..60d4991d5c 100644 --- a/graphql_api/tests/test_coverage_analytics_measurements.py +++ b/graphql_api/tests/test_coverage_analytics_measurements.py @@ -3,104 +3,15 @@ from django.test import TransactionTestCase, override_settings from django.utils import timezone -from freezegun import freeze_time from codecov_auth.tests.factories import OwnerFactory from core.tests.factories import ( - CommitFactory, RepositoryFactory, ) from timeseries.models import Interval from .helper import GraphQLTestHelper -query_coverage_analytics_with_interval = """ -query CoverageAnalytics($owner:String!, $repo: String!, $interval: MeasurementInterval!) { - owner(username:$owner) { - repository(name: $repo) { - __typename - ... on Repository { - name - coverageAnalytics { - %s - } - } - ... on ResolverError { - message - } - } - } -} -""" - -default_coverage_analytics_measurements_fields = """ - measurements(interval: $interval) { - timestamp - avg - min - max - } -""" - - -class TestFetchCoverageAnalyticsWithInterval(GraphQLTestHelper, TransactionTestCase): - databases = {'default', 'timeseries'} - - def fetch_coverage_analytics( - self, repo_name, fields=None, interval=None - ): - query = query_coverage_analytics_with_interval % (fields or default_coverage_analytics_measurements_fields) - variables = {"owner": "codecov-user", "repo": repo_name, "interval": interval} - - return self.gql_request(query=query, owner=self.owner, variables=variables) - - def setUp(self): - self.owner = OwnerFactory(username="codecov-user") - self.yaml = {"test": "test"} - - @freeze_time("2022-01-02") - def test_coverage_analytics_with_interval(self): - """Test with interval argument to fetch coverage data in a specific time range""" - # Create repo, commit, and coverage data within different intervals - repo = RepositoryFactory( - author=self.owner, - active=True, - private=True, - name="test", - yaml=self.yaml, - language="erlang", - languages=[], - ) - one_day_ago = timezone.make_aware(datetime.datetime(2022, 1, 1, 0, 0)) - CommitFactory(repository=repo, totals={"c": 65, "h": 20, "m": 5, "n": 25}, timestamp=one_day_ago) - - two_days_ago = timezone.make_aware(datetime.datetime(2022, 1, 2, 0, 0)) - CommitFactory(repository=repo, totals={"c": 75, "h": 30, "m": 10, "n": 40}, timestamp=two_days_ago) - - # Save repository - repo.updatestamp = timezone.now() - repo.save() - self.assertTrue( - repo.pk, "Repository should be saved and have a primary key." - ) - - # Fetch the coverage analytics data with interval - interval = "INTERVAL_1_DAY" - coverage_analytics_data = self.fetch_coverage_analytics(repo.name, default_coverage_analytics_measurements_fields, interval=interval) - - # Define the expected response based on the interval - expected_response = { - "__typename": "Repository", - "name": repo.name, - "coverageAnalytics": { - "measurements": [ - {'avg': 65.0, 'max': 65.0, 'min': 65.0, 'timestamp': '2022-01-01T00:00:00+00:00'}, - {'avg': 75.0, 'max': 75.0, 'min': 75.0, 'timestamp': '2022-01-02T00:00:00+00:00'} - ] - }, - } - assert coverage_analytics_data["owner"]["repository"] == expected_response - @patch("timeseries.helpers.repository_coverage_measurements_with_fallback") class TestMeasurement(TransactionTestCase, GraphQLTestHelper): diff --git a/graphql_api/tests/test_repository.py b/graphql_api/tests/test_repository.py index 307445f1f7..aa518cfe7a 100644 --- a/graphql_api/tests/test_repository.py +++ b/graphql_api/tests/test_repository.py @@ -864,7 +864,7 @@ def test_fetch_is_github_rate_limited_but_errors( assert data["me"]["owner"]["repository"]["isGithubRateLimited"] is None - mock_log_warning.assert_called_once_with( + mock_log_warning.assert_any_call( "Error when checking rate limit", extra={ "repo_id": repo.repoid, diff --git a/graphql_api/types/coverage_analytics/coverage_analytics.graphql b/graphql_api/types/coverage_analytics/coverage_analytics.graphql index 792bd445d3..29346d2db2 100644 --- a/graphql_api/types/coverage_analytics/coverage_analytics.graphql +++ b/graphql_api/types/coverage_analytics/coverage_analytics.graphql @@ -2,16 +2,16 @@ CoverageAnalytics is information related to a repo's test coverage """ type CoverageAnalytics { - "Hits is the number of hits in a coverage report" - hits: Int # formerly repo.hits - "Misses is the number of misses in a coverage report" - misses: Int # formerly repo.misses - "Lines is the number of lines in a coverage report" - lines: Int # formerly repo.lines - "Commit sha is the sha hash of the commit in the coverage report" - commitSha: String # formerly repo.coverageSha + "Hits is the number of hits in the latest commit's coverage report" + hits: Int # formerly repository.hits + "Misses is the number of misses in the latest commit's coverage report" + misses: Int # formerly repository.misses + "Lines is the number of lines in the latest commit's coverage report" + lines: Int # formerly repository.lines + "Commit sha is the sha hash of the commit in the latest commit's coverage report" + commitSha: String # formerly repository.coverageSha "PercentCovered is percent of lines covered (e.g., 87.25)" - percentCovered: Float # formerly repo.coverage + percentCovered: Float # formerly repository.coverage "measurements are points in the time series for coverage over time" measurements( diff --git a/graphql_api/types/coverage_analytics/coverage_analytics.py b/graphql_api/types/coverage_analytics/coverage_analytics.py index 54ca2d5e67..0554a41d04 100644 --- a/graphql_api/types/coverage_analytics/coverage_analytics.py +++ b/graphql_api/types/coverage_analytics/coverage_analytics.py @@ -77,7 +77,7 @@ def resolve_lines( async def resolve_measurements( parent: CoverageAnalyticsProps, info: GraphQLResolveInfo, - interval: Optional[Interval] = None, + interval: Interval, before: Optional[datetime] = None, after: Optional[datetime] = None, branch: Optional[str] = None, diff --git a/graphql_api/types/repository/repository.graphql b/graphql_api/types/repository/repository.graphql index 4a8ef0484d..96f62e6797 100644 --- a/graphql_api/types/repository/repository.graphql +++ b/graphql_api/types/repository/repository.graphql @@ -47,7 +47,6 @@ type Repository { last: Int before: String ): BranchConnection @cost(complexity: 3, multipliers: ["first", "last"]) - # flags to be removed with #2282 flags( filters: FlagSetFilters orderingDirection: OrderingDirection @@ -103,12 +102,7 @@ type Repository { ): TestResultConnection! @cost(complexity: 10, multipliers: ["first", "last"]) "CoverageAnalytics are fields related to Codecov's Coverage product offering" - coverageAnalytics( - interval: MeasurementInterval - after: DateTime - before: DateTime - branch: String - ): CoverageAnalytics + coverageAnalytics: CoverageAnalytics } type TestResultConnection { diff --git a/mypy.ini b/mypy.ini index aaae96706a..1494f91bad 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,4 +1,5 @@ # Global options: + [mypy] disallow_untyped_defs = True ignore_missing_imports = True @@ -6,11 +7,5 @@ disable_error_code = attr-defined,import-untyped,name-defined follow_imports = silent warn_no_return = False -# Enable async-related checks -strict = True # Enforces stricter rules including disallowing any sync-to-async mismatches -warn_unawaited = True # Warns if an async function call is not awaited -check_untyped_defs = True # Checks untyped functions, especially in async context - -# Per-module configuration for tests [mypy-*.tests.*] -disallow_untyped_defs = False +disallow_untyped_defs = False \ No newline at end of file diff --git a/ruff.toml b/ruff.toml index 64fc63a2a0..4c03a80c73 100644 --- a/ruff.toml +++ b/ruff.toml @@ -35,26 +35,30 @@ indent-width = 4 # Assume Python 3.12 target-version = "py312" -lint.select = [ - "F", # pyflakes - general Python errors, undefined names - "I", # isort - import sorting - "E", # pycodestyle - error rules - "W", # pycodestyle - warning rules - "PLC", # pylint - convention rules - "PLE", # pylint - error rules - "PERF", # perflint - "SIM", # flake8-simplify - async checks, including SIM117 - "PIE", # flake8-pie - best practices, including PIE804 for async checks -] - -# Ignored linting rules -lint.ignore = ["F405", "F403", "E501", "E712"] +[lint] +# Currently only enabled for F (Pyflakes), I (isort), E,W (pycodestyle:Error/Warning), PLC/PLE (Pylint:Convention/Error) +# and PERF (Perflint) rules: https://docs.astral.sh/ruff/rules/ +select = ["F", "I", "E", "W", "PLC", "PLE", "PERF"] +ignore = ["F405", "F403", "E501", "E712"] # Allow fix for all enabled rules (when `--fix`) is provided. # The preferred method (for now) w.r.t. fixable rules is to manually update the makefile # with --fix and re-run 'make lint' -lint.fixable = ["ALL"] -lint.unfixable = [] +fixable = ["ALL"] +unfixable = [] # Allow unused variables when underscore-prefixed. -lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[format] +# Like Black, use double quotes for strings. +quote-style = "double" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" \ No newline at end of file