From 019ba4cbd2958b38fdcc8f1758ec27c73048fad9 Mon Sep 17 00:00:00 2001 From: tdstein Date: Thu, 25 Jul 2024 22:24:53 -0400 Subject: [PATCH 01/10] refactor: class facade for urls module --- src/posit/connect/urls.py | 52 +++++++++++---------------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/src/posit/connect/urls.py b/src/posit/connect/urls.py index fdf6884a..c6bd71f1 100644 --- a/src/posit/connect/urls.py +++ b/src/posit/connect/urls.py @@ -5,48 +5,24 @@ class Url(str): - """URL representation for Connect. + def __new__(cls, value): + if not isinstance(value, str): + raise ValueError("Value must be a string") + return super(Url, cls).__new__(cls, create(value)) - An opinionated URL representation of a Connect URL. Maintains various - conventions: - - It begins with a scheme. - - It is absolute. - - It contains '__api__'. - - Supports Python builtin __add__ for append. - - Methods - ------- - append(path: str) - Append a path to the URL. - - Examples - -------- - >>> url = Url("http://connect.example.com/") - http://connect.example.com/__api__ - >>> url + "endpoint" - http://connect.example.com/__api__/endpoint - - Append works with string-like objects (e.g., objects that support casting to string) - >>> url = Url("http://connect.example.com/__api__/endpoint") - http://connect.example.com/__api__/endpoint - >>> url + 1 - http://connect.example.com/__api__/endpoint/1 - """ - - def __new__(cls, value: str): - url = _create(value) - return super(Url, cls).__new__(cls, url) + def __init__(self, value): + # Call the parent class's __init__ method + super(Url, self).__init__() def __add__(self, path: str): return self.append(path) def append(self, path: str) -> Url: - return Url(_append(self, path)) + return Url(append(self, path)) -def _create(url: str) -> str: - """Create a URL. +def create(url: str) -> str: + """Create a Url. Asserts that the URL is a proper Posit Connect endpoint. The path '__api__' is appended to the URL if it is missing. @@ -87,12 +63,12 @@ def _create(url: str) -> str: url = url.rstrip("/") if "/__api__" not in url: - url = _append(url, "__api__") + url = append(url, "__api__") return url -def _append(url: str, path) -> str: +def append(url: str, path: str) -> str: """Append a path to a Url. Parameters @@ -109,8 +85,8 @@ def _append(url: str, path) -> str: Examples -------- - >>> url = _create("http://example.com/__api__") - >>> _append(url, "path") + >>> url = urls.create("http://example.com/__api__") + >>> url + "path" http://example.com/__api__/path """ path = str(path).strip("/") From 48a170837e59ad1bc9c932c5faefb753f6c7fbde Mon Sep 17 00:00:00 2001 From: tdstein Date: Thu, 25 Jul 2024 22:26:01 -0400 Subject: [PATCH 02/10] refactor: mark urls module methods as internal --- src/posit/connect/urls.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/posit/connect/urls.py b/src/posit/connect/urls.py index c6bd71f1..21bcc774 100644 --- a/src/posit/connect/urls.py +++ b/src/posit/connect/urls.py @@ -8,20 +8,19 @@ class Url(str): def __new__(cls, value): if not isinstance(value, str): raise ValueError("Value must be a string") - return super(Url, cls).__new__(cls, create(value)) + return super(Url, cls).__new__(cls, _create(value)) def __init__(self, value): - # Call the parent class's __init__ method super(Url, self).__init__() def __add__(self, path: str): return self.append(path) def append(self, path: str) -> Url: - return Url(append(self, path)) + return Url(_append(self, path)) -def create(url: str) -> str: +def _create(url: str) -> str: """Create a Url. Asserts that the URL is a proper Posit Connect endpoint. The path '__api__' is appended to the URL if it is missing. @@ -63,12 +62,12 @@ def create(url: str) -> str: url = url.rstrip("/") if "/__api__" not in url: - url = append(url, "__api__") + url = _append(url, "__api__") return url -def append(url: str, path: str) -> str: +def _append(url: str, path: str) -> str: """Append a path to a Url. Parameters From c17dcfaa06763f24aca3d92cbb06648edbbd7586 Mon Sep 17 00:00:00 2001 From: tdstein Date: Thu, 25 Jul 2024 22:47:43 -0400 Subject: [PATCH 03/10] docs: add docstrings for url class --- src/posit/connect/urls.py | 66 ++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/src/posit/connect/urls.py b/src/posit/connect/urls.py index 21bcc774..eb7dcc3d 100644 --- a/src/posit/connect/urls.py +++ b/src/posit/connect/urls.py @@ -5,9 +5,54 @@ class Url(str): - def __new__(cls, value): - if not isinstance(value, str): - raise ValueError("Value must be a string") + """URL representation for Connect. + + An opinionated URL representation of a Connect URL. Maintains various + conventions: + - It begins with a scheme. + - It is absolute. + - It contains '__api__'. + + Supports Python builtin __add__ for append. + + Methods + ------- + append(path: str) + Append a path to the URL. + + Examples + -------- + >>> url = Url("http://connect.example.com/) + http://connect.example.com/__api__ + >>> url + "endpoint" + http://connect.example.com/__api__/endpoint + + Append works with string-like objects (e.g., objects that support casting to string) + >>> url = Url("http://connect.example.com/__api__/endpoint) + http://connect.example.com/__api__/endpoint + >>> url + 1 + http://connect.example.com/__api__/endpoint/1 + """ + + def __new__(cls, value: str): + """New. + + Parameters + ---------- + value : str + Any URL. + + Returns + ------- + Url + + Raises + ------ + ValueError + `value` is missing a scheme. + ValueError + `value` is missing a network location (i.e., a domain name). + """ return super(Url, cls).__new__(cls, _create(value)) def __init__(self, value): @@ -21,7 +66,7 @@ def append(self, path: str) -> Url: def _create(url: str) -> str: - """Create a Url. + """Create a URL. Asserts that the URL is a proper Posit Connect endpoint. The path '__api__' is appended to the URL if it is missing. @@ -67,7 +112,7 @@ def _create(url: str) -> str: return url -def _append(url: str, path: str) -> str: +def _append(url: str, path) -> str: """Append a path to a Url. Parameters @@ -84,11 +129,16 @@ def _append(url: str, path: str) -> str: Examples -------- - >>> url = urls.create("http://example.com/__api__") - >>> url + "path" + >>> url = _create("http://example.com/__api__") + >>> _append(url, "path") http://example.com/__api__/path """ - path = str(path).strip("/") + path = str(path) + # Removes leading '/' from path to avoid double slashes. + path = path.lstrip("/") + # Removes trailing '/' from path to avoid double slashes. + path = path.rstrip("/") + # See https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlsplit split = urlsplit(url, allow_fragments=False) new_path = posixpath.join(split.path, path) return urlunsplit( From ba0486350314496ecd6b9f76a5b299c5ace4263a Mon Sep 17 00:00:00 2001 From: tdstein Date: Thu, 25 Jul 2024 22:56:59 -0400 Subject: [PATCH 04/10] refactor: optimizations --- src/posit/connect/urls.py | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/src/posit/connect/urls.py b/src/posit/connect/urls.py index eb7dcc3d..fdf6884a 100644 --- a/src/posit/connect/urls.py +++ b/src/posit/connect/urls.py @@ -22,41 +22,21 @@ class Url(str): Examples -------- - >>> url = Url("http://connect.example.com/) + >>> url = Url("http://connect.example.com/") http://connect.example.com/__api__ >>> url + "endpoint" http://connect.example.com/__api__/endpoint Append works with string-like objects (e.g., objects that support casting to string) - >>> url = Url("http://connect.example.com/__api__/endpoint) + >>> url = Url("http://connect.example.com/__api__/endpoint") http://connect.example.com/__api__/endpoint >>> url + 1 http://connect.example.com/__api__/endpoint/1 """ def __new__(cls, value: str): - """New. - - Parameters - ---------- - value : str - Any URL. - - Returns - ------- - Url - - Raises - ------ - ValueError - `value` is missing a scheme. - ValueError - `value` is missing a network location (i.e., a domain name). - """ - return super(Url, cls).__new__(cls, _create(value)) - - def __init__(self, value): - super(Url, self).__init__() + url = _create(value) + return super(Url, cls).__new__(cls, url) def __add__(self, path: str): return self.append(path) @@ -133,12 +113,7 @@ def _append(url: str, path) -> str: >>> _append(url, "path") http://example.com/__api__/path """ - path = str(path) - # Removes leading '/' from path to avoid double slashes. - path = path.lstrip("/") - # Removes trailing '/' from path to avoid double slashes. - path = path.rstrip("/") - # See https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlsplit + path = str(path).strip("/") split = urlsplit(url, allow_fragments=False) new_path = posixpath.join(split.path, path) return urlunsplit( From c2a720fce6147510ab9627e776f74c9ce5f58059 Mon Sep 17 00:00:00 2001 From: tdstein Date: Fri, 26 Jul 2024 10:25:56 -0400 Subject: [PATCH 05/10] build: add import formatting with autoflake and isort --- Makefile | 2 ++ requirements-dev.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 3191f435..9d9301d5 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,8 @@ docs: fmt: $(PYTHON) -m ruff check --fix $(PYTHON) -m ruff format . + $(PYTHON) -m autoflake --remove-all-unused-imports --in-place --recursive . + $(PYTHON) -m isort . install: $(PIP) install dist/*.whl diff --git a/requirements-dev.txt b/requirements-dev.txt index 92cc2a3d..7c5680a7 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,7 @@ +autoflake build coverage +isort mypy pandas pre-commit From a98f7569c8485deb77560919cd1eb0beb8716359 Mon Sep 17 00:00:00 2001 From: tdstein Date: Fri, 26 Jul 2024 11:46:16 -0400 Subject: [PATCH 06/10] use ruff instead of isort, and combine make fmt and make fix. --- Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 9d9301d5..e70d1c90 100644 --- a/Makefile +++ b/Makefile @@ -39,10 +39,9 @@ docs: $(MAKE) -C ./docs fmt: - $(PYTHON) -m ruff check --fix - $(PYTHON) -m ruff format . $(PYTHON) -m autoflake --remove-all-unused-imports --in-place --recursive . - $(PYTHON) -m isort . + $(PYTHON) -m ruff check --select I --fix + $(PYTHON) -m ruff format . install: $(PIP) install dist/*.whl From 844379f178066c41548c8b7ff10f4d878c53f2b7 Mon Sep 17 00:00:00 2001 From: tdstein Date: Fri, 26 Jul 2024 11:48:34 -0400 Subject: [PATCH 07/10] move ruff rule selection to pyproject.toml --- Makefile | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index e70d1c90..44528e01 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ docs: fmt: $(PYTHON) -m autoflake --remove-all-unused-imports --in-place --recursive . - $(PYTHON) -m ruff check --select I --fix + $(PYTHON) -m ruff check --fix $(PYTHON) -m ruff format . install: diff --git a/pyproject.toml b/pyproject.toml index a2989b6b..58860fa0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ docstring-code-format = true docstring-code-line-length = "dynamic" [tool.ruff.lint] -select = ["D", "F401", "I"] +select = ["D", "I"] ignore = [ # NumPy style docstring convention with noted exceptions. # https://docs.astral.sh/ruff/faq/#does-ruff-support-numpy-or-google-style-docstrings From 00396a35b467001811a5fcb88824ae029173c194 Mon Sep 17 00:00:00 2001 From: tdstein Date: Fri, 26 Jul 2024 11:52:13 -0400 Subject: [PATCH 08/10] replace autoflake8 with ruff rule F401 --- Makefile | 1 - pyproject.toml | 2 +- requirements-dev.txt | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 44528e01..3191f435 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,6 @@ docs: $(MAKE) -C ./docs fmt: - $(PYTHON) -m autoflake --remove-all-unused-imports --in-place --recursive . $(PYTHON) -m ruff check --fix $(PYTHON) -m ruff format . diff --git a/pyproject.toml b/pyproject.toml index 58860fa0..a2989b6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ docstring-code-format = true docstring-code-line-length = "dynamic" [tool.ruff.lint] -select = ["D", "I"] +select = ["D", "F401", "I"] ignore = [ # NumPy style docstring convention with noted exceptions. # https://docs.astral.sh/ruff/faq/#does-ruff-support-numpy-or-google-style-docstrings diff --git a/requirements-dev.txt b/requirements-dev.txt index 7c5680a7..92cc2a3d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,5 @@ -autoflake build coverage -isort mypy pandas pre-commit From 571177b01b3b3e2c6381327f2f185de1462aa776 Mon Sep 17 00:00:00 2001 From: tdstein Date: Fri, 26 Jul 2024 12:12:57 -0400 Subject: [PATCH 09/10] chore: apply flake8 builtin rules --- examples/connect/shiny-python/app.py | 2 +- pyproject.toml | 2 +- src/posit/connect/bundles.py | 22 ++++++------ src/posit/connect/cursors.py | 12 +++---- src/posit/connect/permissions.py | 6 ++-- src/posit/connect/tasks.py | 14 ++++---- src/posit/connect/users.py | 4 +-- tests/posit/connect/test_client.py | 6 ++-- tests/posit/connect/test_env.py | 4 +-- tests/posit/connect/test_permissions.py | 34 +++++++++--------- tests/posit/connect/test_tasks.py | 46 ++++++++++++------------- 11 files changed, 76 insertions(+), 76 deletions(-) diff --git a/examples/connect/shiny-python/app.py b/examples/connect/shiny-python/app.py index 4a1cb695..bf97579e 100644 --- a/examples/connect/shiny-python/app.py +++ b/examples/connect/shiny-python/app.py @@ -16,7 +16,7 @@ app_ui = ui.page_fluid(ui.output_text("text"), ui.output_data_frame("result")) -def server(input: Inputs, output: Outputs, session: Session): +def server(i: Inputs, o: Outputs, session: Session): """ Shiny for Python example application that shows user information and the first few rows from a table hosted in Databricks. diff --git a/pyproject.toml b/pyproject.toml index a2989b6b..a664181d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ docstring-code-format = true docstring-code-line-length = "dynamic" [tool.ruff.lint] -select = ["D", "F401", "I"] +select = ["A", "D", "F401", "I"] ignore = [ # NumPy style docstring convention with noted exceptions. # https://docs.astral.sh/ruff/faq/#does-ruff-support-numpy-or-google-style-docstrings diff --git a/src/posit/connect/bundles.py b/src/posit/connect/bundles.py index fa6eb194..b38ea7dd 100644 --- a/src/posit/connect/bundles.py +++ b/src/posit/connect/bundles.py @@ -235,7 +235,7 @@ def __init__( super().__init__(params) self.content_guid = content_guid - def create(self, input: io.BufferedReader | bytes | str) -> Bundle: + def create(self, archive: io.BufferedReader | bytes | str) -> Bundle: """ Create a bundle. @@ -243,8 +243,8 @@ def create(self, input: io.BufferedReader | bytes | str) -> Bundle: Parameters ---------- - input : io.BufferedReader, bytes, or str - Input archive for bundle creation. A 'str' type assumes a relative or absolute filepath. + archive : io.BufferedReader, bytes, or str + Archive for bundle creation. A 'str' type assumes a relative or absolute filepath. Returns ------- @@ -273,14 +273,14 @@ def create(self, input: io.BufferedReader | bytes | str) -> Bundle: >>> bundle.create("bundle.tar.gz") None """ - if isinstance(input, (io.BufferedReader, bytes)): - data = input - elif isinstance(input, str): - with open(input, "rb") as file: + if isinstance(archive, (io.BufferedReader, bytes)): + data = archive + elif isinstance(archive, str): + with open(archive, "rb") as file: data = file.read() else: raise TypeError( - f"create() expected argument type 'io.BufferedReader', 'bytes', or 'str', but got '{type(input).__name__}'" + f"create() expected argument type 'io.BufferedReader', 'bytes', or 'str', but got '{type(archive).__name__}'" ) path = f"v1/content/{self.content_guid}/bundles" @@ -314,12 +314,12 @@ def find_one(self) -> Bundle | None: bundles = self.find() return next(iter(bundles), None) - def get(self, id: str) -> Bundle: + def get(self, uid: str) -> Bundle: """Get a bundle. Parameters ---------- - id : str + uid : str Identifier of the bundle to retrieve. Returns @@ -327,7 +327,7 @@ def get(self, id: str) -> Bundle: Bundle The bundle with the specified ID. """ - path = f"v1/content/{self.content_guid}/bundles/{id}" + path = f"v1/content/{self.content_guid}/bundles/{uid}" url = self.url + path response = self.session.get(url) result = response.json() diff --git a/src/posit/connect/cursors.py b/src/posit/connect/cursors.py index 98a039d6..ec8cf44e 100644 --- a/src/posit/connect/cursors.py +++ b/src/posit/connect/cursors.py @@ -45,17 +45,17 @@ def fetch_pages(self) -> Generator[CursorPage, None, None]: ------ Generator[Page, None, None] """ - next = None + next_page = None while True: - page = self.fetch_page(next) + page = self.fetch_page(next_page) yield page cursors: dict = page.paging.get("cursors", {}) - next = cursors.get("next") - if not next: + next_page = cursors.get("next") + if not next_page: # stop if a next page is not defined return - def fetch_page(self, next: str | None = None) -> CursorPage: + def fetch_page(self, next_page: str | None = None) -> CursorPage: """Fetch a page. Parameters @@ -69,7 +69,7 @@ def fetch_page(self, next: str | None = None) -> CursorPage: """ params = { **self.params, - "next": next, + "next": next_page, "limit": _MAX_PAGE_SIZE, } response = self.session.get(self.url, params=params) diff --git a/src/posit/connect/permissions.py b/src/posit/connect/permissions.py index 118d7ba4..54f1e296 100644 --- a/src/posit/connect/permissions.py +++ b/src/posit/connect/permissions.py @@ -149,19 +149,19 @@ def find_one(self, *args, **kwargs) -> Permission | None: permissions = self.find(*args, **kwargs) return next(iter(permissions), None) - def get(self, id: str) -> Permission: + def get(self, uid: str) -> Permission: """Get a permission. Parameters ---------- - id : str + uid : str The permission id. Returns ------- Permission """ - path = f"v1/content/{self.content_guid}/permissions/{id}" + path = f"v1/content/{self.content_guid}/permissions/{uid}" url = self.url + path response = self.session.get(url) return Permission(self.params, **response.json()) diff --git a/src/posit/connect/tasks.py b/src/posit/connect/tasks.py index 83d17b3b..3e735ab1 100644 --- a/src/posit/connect/tasks.py +++ b/src/posit/connect/tasks.py @@ -144,12 +144,12 @@ def wait_for(self) -> None: class Tasks(resources.Resources): @overload - def get(self, id: str, first: int, wait: int) -> Task: + def get(self, uid: str, first: int, wait: int) -> Task: """Get a task. Parameters ---------- - id : str + uid : str Task identifier. first : int, default 0 Line to start output on. @@ -163,12 +163,12 @@ def get(self, id: str, first: int, wait: int) -> Task: ... @overload - def get(self, id: str, *args, **kwargs) -> Task: + def get(self, uid: str, *args, **kwargs) -> Task: """Get a task. Parameters ---------- - id : str + uid : str Task identifier. Returns @@ -177,12 +177,12 @@ def get(self, id: str, *args, **kwargs) -> Task: """ ... - def get(self, id: str, *args, **kwargs) -> Task: + def get(self, uid: str, *args, **kwargs) -> Task: """Get a task. Parameters ---------- - id : str + uid : str Task identifier. Returns @@ -190,7 +190,7 @@ def get(self, id: str, *args, **kwargs) -> Task: Task """ params = dict(*args, **kwargs) - path = f"v1/tasks/{id}" + path = f"v1/tasks/{uid}" url = self.url + path response = self.session.get(url, params=params) result = response.json() diff --git a/src/posit/connect/users.py b/src/posit/connect/users.py index cccb72ad..e8cc26aa 100644 --- a/src/posit/connect/users.py +++ b/src/posit/connect/users.py @@ -230,8 +230,8 @@ def find_one(self, *args, **kwargs) -> User | None: ) return next(users, None) - def get(self, id: str) -> User: - url = self.url + f"v1/users/{id}" + def get(self, uid: str) -> User: + url = self.url + f"v1/users/{uid}" response = self.session.get(url) return User( self.params, diff --git a/tests/posit/connect/test_client.py b/tests/posit/connect/test_client.py index e873ce8e..efe716e3 100644 --- a/tests/posit/connect/test_client.py +++ b/tests/posit/connect/test_client.py @@ -7,19 +7,19 @@ from .api import load_mock # type: ignore -@pytest.fixture +@pytest.fixture() def MockAuth(): with patch("posit.connect.client.Auth") as mock: yield mock -@pytest.fixture +@pytest.fixture() def MockConfig(): with patch("posit.connect.client.Config") as mock: yield mock -@pytest.fixture +@pytest.fixture() def MockSession(): with patch("posit.connect.client.Session") as mock: yield mock diff --git a/tests/posit/connect/test_env.py b/tests/posit/connect/test_env.py index afd0367c..418e5127 100644 --- a/tests/posit/connect/test_env.py +++ b/tests/posit/connect/test_env.py @@ -304,10 +304,10 @@ def test_find(): content = c.content.get(guid) # invoke - vars = content.environment_variables.find() + envvars = content.environment_variables.find() # assert - assert vars == ["TEST"] + assert envvars == ["TEST"] assert mock_get_content.call_count == 1 assert mock_get_environment.call_count == 1 diff --git a/tests/posit/connect/test_permissions.py b/tests/posit/connect/test_permissions.py index 03099af9..2f37879a 100644 --- a/tests/posit/connect/test_permissions.py +++ b/tests/posit/connect/test_permissions.py @@ -15,12 +15,12 @@ class TestPermissionDelete: @responses.activate def test(self): # data - id = "94" + uid = "94" content_guid = "f2f37341-e21d-3d80-c698-a935ad614066" # behavior mock_delete = responses.delete( - f"https://connect.example/__api__/v1/content/{content_guid}/permissions/{id}" + f"https://connect.example/__api__/v1/content/{content_guid}/permissions/{uid}" ) # setup @@ -28,7 +28,7 @@ def test(self): requests.Session(), Url("https://connect.example/__api__") ) fake_permission = load_mock( - f"v1/content/{content_guid}/permissions/{id}.json" + f"v1/content/{content_guid}/permissions/{uid}.json" ) permission = Permission(params, **fake_permission) @@ -43,7 +43,7 @@ class TestPermissionUpdate: @responses.activate def test_request_shape(self): # test data - id = random.randint(0, 100) + uid = random.randint(0, 100) content_guid = str(uuid.uuid4()) principal_guid = str(uuid.uuid4()) principal_type = "principal_type" @@ -52,7 +52,7 @@ def test_request_shape(self): # define api behavior responses.put( - f"https://connect.example/__api__/v1/content/{content_guid}/permissions/{id}", + f"https://connect.example/__api__/v1/content/{content_guid}/permissions/{uid}", json={ # doesn't matter for this test }, @@ -77,7 +77,7 @@ def test_request_shape(self): ) permission = Permission( params, - id=id, + id=uid, content_guid=content_guid, principal_guid=principal_guid, principal_type=principal_type, @@ -94,18 +94,18 @@ def test_role_update(self): old_role = "owner" new_role = "viewer" - id = "94" + uid = "94" content_guid = "f2f37341-e21d-3d80-c698-a935ad614066" fake_permission = load_mock( - f"v1/content/{content_guid}/permissions/{id}.json" + f"v1/content/{content_guid}/permissions/{uid}.json" ) fake_permission.update(role=new_role) # define api behavior - id = random.randint(0, 100) + uid = random.randint(0, 100) content_guid = str(uuid.uuid4()) responses.put( - f"https://connect.example/__api__/v1/content/{content_guid}/permissions/{id}", + f"https://connect.example/__api__/v1/content/{content_guid}/permissions/{uid}", json=fake_permission, match=[ matchers.json_params_matcher( @@ -123,7 +123,7 @@ def test_role_update(self): requests.Session(), Url("https://connect.example/__api__") ) permission = Permission( - params, id=id, content_guid=content_guid, role=old_role + params, id=uid, content_guid=content_guid, role=old_role ) # assert role change with respect to api response @@ -164,13 +164,13 @@ class TestPermissionsCreate: @responses.activate def test(self): # data - id = "94" + uid = "94" content_guid = "f2f37341-e21d-3d80-c698-a935ad614066" principal_guid = str(uuid.uuid4()) principal_type = "user" role = "owner" fake_permission = { - **load_mock(f"v1/content/{content_guid}/permissions/{id}.json"), + **load_mock(f"v1/content/{content_guid}/permissions/{uid}.json"), "principal_guid": principal_guid, "principal_type": principal_type, "role": role, @@ -268,15 +268,15 @@ class TestPermissionsGet: @responses.activate def test(self): # data - id = "94" + uid = "94" content_guid = "f2f37341-e21d-3d80-c698-a935ad614066" fake_permission = load_mock( - f"v1/content/{content_guid}/permissions/{id}.json" + f"v1/content/{content_guid}/permissions/{uid}.json" ) # behavior responses.get( - f"https://connect.example/__api__/v1/content/{content_guid}/permissions/{id}", + f"https://connect.example/__api__/v1/content/{content_guid}/permissions/{uid}", json=fake_permission, ) @@ -287,7 +287,7 @@ def test(self): permissions = Permissions(params, content_guid=content_guid) # invoke - permission = permissions.get(id) + permission = permissions.get(uid) # assert assert permission == fake_permission diff --git a/tests/posit/connect/test_tasks.py b/tests/posit/connect/test_tasks.py index 134d8017..bd09a34d 100644 --- a/tests/posit/connect/test_tasks.py +++ b/tests/posit/connect/test_tasks.py @@ -43,23 +43,23 @@ def test_result(self): class TestTaskUpdate: @responses.activate def test(self): - id = "jXhOhdm5OOSkGhJw" + uid = "jXhOhdm5OOSkGhJw" # behavior mock_tasks_get = [0] * 2 mock_tasks_get[0] = responses.get( - f"https://connect.example/__api__/v1/tasks/{id}", - json={**load_mock(f"v1/tasks/{id}.json"), "finished": False}, + f"https://connect.example/__api__/v1/tasks/{uid}", + json={**load_mock(f"v1/tasks/{uid}.json"), "finished": False}, ) mock_tasks_get[1] = responses.get( - f"https://connect.example/__api__/v1/tasks/{id}", - json={**load_mock(f"v1/tasks/{id}.json"), "finished": True}, + f"https://connect.example/__api__/v1/tasks/{uid}", + json={**load_mock(f"v1/tasks/{uid}.json"), "finished": True}, ) # setup c = connect.Client("https://connect.example", "12345") - task = c.tasks.get(id) + task = c.tasks.get(uid) assert not task.is_finished # invoke @@ -72,25 +72,25 @@ def test(self): @responses.activate def test_with_params(self): - id = "jXhOhdm5OOSkGhJw" + uid = "jXhOhdm5OOSkGhJw" params = {"first": 10, "wait": 10} # behavior mock_tasks_get = [0] * 2 mock_tasks_get[0] = responses.get( - f"https://connect.example/__api__/v1/tasks/{id}", - json={**load_mock(f"v1/tasks/{id}.json"), "finished": False}, + f"https://connect.example/__api__/v1/tasks/{uid}", + json={**load_mock(f"v1/tasks/{uid}.json"), "finished": False}, ) mock_tasks_get[1] = responses.get( - f"https://connect.example/__api__/v1/tasks/{id}", - json={**load_mock(f"v1/tasks/{id}.json"), "finished": True}, + f"https://connect.example/__api__/v1/tasks/{uid}", + json={**load_mock(f"v1/tasks/{uid}.json"), "finished": True}, match=[matchers.query_param_matcher(params)], ) # setup c = connect.Client("https://connect.example", "12345") - task = c.tasks.get(id) + task = c.tasks.get(uid) assert not task.is_finished # invoke @@ -105,23 +105,23 @@ def test_with_params(self): class TestTaskWaitFor: @responses.activate def test(self): - id = "jXhOhdm5OOSkGhJw" + uid = "jXhOhdm5OOSkGhJw" # behavior mock_tasks_get = [0] * 2 mock_tasks_get[0] = responses.get( - f"https://connect.example/__api__/v1/tasks/{id}", - json={**load_mock(f"v1/tasks/{id}.json"), "finished": False}, + f"https://connect.example/__api__/v1/tasks/{uid}", + json={**load_mock(f"v1/tasks/{uid}.json"), "finished": False}, ) mock_tasks_get[1] = responses.get( - f"https://connect.example/__api__/v1/tasks/{id}", - json={**load_mock(f"v1/tasks/{id}.json"), "finished": True}, + f"https://connect.example/__api__/v1/tasks/{uid}", + json={**load_mock(f"v1/tasks/{uid}.json"), "finished": True}, ) # setup c = connect.Client("https://connect.example", "12345") - task = c.tasks.get(id) + task = c.tasks.get(uid) assert not task.is_finished # invoke @@ -136,20 +136,20 @@ def test(self): class TestTasksGet: @responses.activate def test(self): - id = "jXhOhdm5OOSkGhJw" + uid = "jXhOhdm5OOSkGhJw" # behavior mock_tasks_get = responses.get( - f"https://connect.example/__api__/v1/tasks/{id}", - json={**load_mock(f"v1/tasks/{id}.json"), "finished": False}, + f"https://connect.example/__api__/v1/tasks/{uid}", + json={**load_mock(f"v1/tasks/{uid}.json"), "finished": False}, ) # setup c = connect.Client("https://connect.example", "12345") # invoke - task = c.tasks.get(id) + task = c.tasks.get(uid) # assert - assert task.id == id + assert task.id == uid assert mock_tasks_get.call_count == 1 From 00444ad31795d8a19d85c4039b8332805438c61e Mon Sep 17 00:00:00 2001 From: tdstein Date: Tue, 30 Jul 2024 09:30:30 -0400 Subject: [PATCH 10/10] add comments to linting rules --- pyproject.toml | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a664181d..3a2c3d87 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,32 @@ docstring-code-format = true docstring-code-line-length = "dynamic" [tool.ruff.lint] -select = ["A", "D", "F401", "I"] +select = [ + # flake8-builtins + # https://docs.astral.sh/ruff/rules/#flake8-builtins-a + # + # Check for builtin shadowing (i.e., naming a variable 'for', which is a builtin.) + "A", + + # pydocstyle + # https://docs.astral.sh/ruff/rules/#pydocstyle-d + # https://docs.astral.sh/ruff/faq/#does-ruff-support-numpy-or-google-style-docstrings + # + # Check docstring formatting. Many of these rules are intentionally ignored below. + "D", + + # pyflakes - unused-import + # https://docs.astral.sh/ruff/rules/unused-import/ + # + # Check for unused imports. + "F401", + + # isort + # https://docs.astral.sh/ruff/rules/#isort-i + # + # Sort imports. + "I" +] ignore = [ # NumPy style docstring convention with noted exceptions. # https://docs.astral.sh/ruff/faq/#does-ruff-support-numpy-or-google-style-docstrings @@ -67,7 +92,7 @@ ignore = [ 'D415', 'D416', 'D417', - 'D418', # The Python Language Server can accomdate documentation for individual methods. + 'D418', # The Python Language Server can accomodate documentation for individual methods. # TODO(#135) resarch D418 and determine if we should continue ignoring it. ]