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

Fix up circular imports and typing-only imports #414

Merged
merged 3 commits into from
Dec 6, 2024
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
8 changes: 4 additions & 4 deletions pyairtable/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
__version__ = "3.0.0"

from .api import Api, Base, Table
from .api.enterprise import Enterprise
from .api.retrying import retry_strategy
from .api.workspace import Workspace
from pyairtable.api import Api, Base, Table
from pyairtable.api.enterprise import Enterprise
from pyairtable.api.retrying import retry_strategy
from pyairtable.api.workspace import Workspace

__all__ = [
"Api",
Expand Down
6 changes: 3 additions & 3 deletions pyairtable/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .api import Api
from .base import Base
from .table import Table
from pyairtable.api.api import Api
from pyairtable.api.base import Base
from pyairtable.api.table import Table

__all__ = [
"Api",
Expand Down
22 changes: 10 additions & 12 deletions pyairtable/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
from typing_extensions import TypeAlias

from pyairtable.api import retrying
from pyairtable.api.base import Base
from pyairtable.api.enterprise import Enterprise
from pyairtable.api.params import options_to_json_and_params, options_to_params
from pyairtable.api.table import Table
from pyairtable.api.types import UserAndScopesDict, assert_typed_dict
from pyairtable.api.workspace import Workspace
from pyairtable.models.schema import Bases
Expand Down Expand Up @@ -44,7 +46,7 @@ class Api:
MAX_URL_LENGTH = 16000

# Cached metadata to reduce API calls
_bases: Optional[Dict[str, "pyairtable.api.base.Base"]] = None
_bases: Optional[Dict[str, "Base"]] = None

endpoint_url: Url
session: Session
Expand Down Expand Up @@ -126,7 +128,7 @@ def base(
*,
validate: bool = False,
force: bool = False,
) -> "pyairtable.api.base.Base":
) -> "Base":
"""
Return a new :class:`Base` instance that uses this instance of :class:`Api`.

Expand All @@ -141,7 +143,7 @@ def base(
if validate:
info = self._base_info(force=force).base(base_id)
return self._base_from_info(info)
return pyairtable.api.base.Base(self, base_id)
return Base(self, base_id)

@cache_unless_forced
def _base_info(self) -> Bases:
Expand All @@ -158,15 +160,15 @@ def _base_info(self) -> Bases:
}
return Bases.from_api(data, self)

def _base_from_info(self, base_info: Bases.Info) -> "pyairtable.api.base.Base":
return pyairtable.api.base.Base(
def _base_from_info(self, base_info: Bases.Info) -> "Base":
return Base(
self,
base_info.id,
name=base_info.name,
permission_level=base_info.permission_level,
)

def bases(self, *, force: bool = False) -> List["pyairtable.api.base.Base"]:
def bases(self, *, force: bool = False) -> List["Base"]:
"""
Retrieve the base's schema and return a list of :class:`Base` instances.

Expand All @@ -189,7 +191,7 @@ def create_base(
workspace_id: str,
name: str,
tables: Sequence[Dict[str, Any]],
) -> "pyairtable.api.base.Base":
) -> "Base":
"""
Create a base in the given workspace.

Expand All @@ -210,7 +212,7 @@ def table(
*,
validate: bool = False,
force: bool = False,
) -> "pyairtable.api.table.Table":
) -> "Table":
"""
Build a new :class:`Table` instance that uses this instance of :class:`Api`.

Expand Down Expand Up @@ -407,7 +409,3 @@ def enterprise(self, enterprise_account_id: str) -> Enterprise:
Build an object representing an enterprise account.
"""
return Enterprise(self, enterprise_account_id)


import pyairtable.api.base # noqa
import pyairtable.api.table # noqa
15 changes: 10 additions & 5 deletions pyairtable/api/base.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import warnings
from functools import cached_property
from typing import Any, Dict, List, Optional, Sequence, Union
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Union

import pyairtable.api.api
import pyairtable.api.table
from pyairtable.models.schema import BaseCollaborators, BaseSchema, BaseShares
from pyairtable.models.webhook import (
Expand All @@ -13,6 +12,9 @@
)
from pyairtable.utils import Url, UrlBuilder, cache_unless_forced, enterprise_only

if TYPE_CHECKING:
from pyairtable.api.api import Api


class Base:
"""
Expand All @@ -25,7 +27,7 @@ class Base:
"""

#: The connection to the Airtable API.
api: "pyairtable.api.api.Api"
api: "Api"

#: The base ID, in the format ``appXXXXXXXXXXXXXX``
id: str
Expand Down Expand Up @@ -67,7 +69,7 @@ def interface(self, interface_id: str) -> Url:

def __init__(
self,
api: Union["pyairtable.api.api.Api", str],
api: Union["Api", str],
base_id: str,
*,
name: Optional[str] = None,
Expand Down Expand Up @@ -99,7 +101,10 @@ def __init__(
category=DeprecationWarning,
stacklevel=2,
)
api = pyairtable.api.api.Api(api)

from pyairtable import Api

api = Api(api)

self.api = api
self.id = base_id
Expand Down
22 changes: 15 additions & 7 deletions pyairtable/api/enterprise.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
from datetime import date, datetime
from functools import cached_property, partialmethod
from typing import Any, Dict, Iterable, Iterator, List, Literal, Optional, Union
from typing import (
TYPE_CHECKING,
Any,
Dict,
Iterable,
Iterator,
List,
Literal,
Optional,
Union,
)

import pydantic
from typing_extensions import Self
Expand All @@ -17,6 +27,9 @@
enterprise_only,
)

if TYPE_CHECKING:
from pyairtable.api.api import Api


@enterprise_only
class Enterprise:
Expand Down Expand Up @@ -85,7 +98,7 @@ def remove_user(self, user_id: str) -> Url:

urls = cached_property(_urls)

def __init__(self, api: "pyairtable.api.api.Api", workspace_id: str):
def __init__(self, api: "Api", workspace_id: str):
self.api = api
self.id = workspace_id
self._info: Optional[EnterpriseInfo] = None
Expand Down Expand Up @@ -612,8 +625,3 @@ class MoveWorkspacesResponse(AirtableModel):


rebuild_models(vars())


# These are at the bottom of the module to avoid circular imports
import pyairtable.api.api # noqa
import pyairtable.api.base # noqa
37 changes: 23 additions & 14 deletions pyairtable/api/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,19 @@
import warnings
from functools import cached_property
from pathlib import Path
from typing import Any, Dict, Iterable, Iterator, List, Optional, Union, overload
from typing import (
TYPE_CHECKING,
Any,
Dict,
Iterable,
Iterator,
List,
Optional,
Union,
overload,
)

import pyairtable.models
from pyairtable.api.retrying import Retry
from pyairtable.api.types import (
FieldName,
RecordDeletedDict,
Expand All @@ -25,6 +34,11 @@
from pyairtable.models.schema import FieldSchema, TableSchema, parse_field_schema
from pyairtable.utils import Url, UrlBuilder, is_table_id

if TYPE_CHECKING:
from pyairtable.api.api import Api, TimeoutTuple
from pyairtable.api.base import Base
from pyairtable.api.retrying import Retry


class Table:
"""
Expand All @@ -37,7 +51,7 @@ class Table:
"""

#: The base that this table belongs to.
base: "pyairtable.api.base.Base"
base: "Base"

#: Can be either the table name or the table ID (``tblXXXXXXXXXXXXXX``).
name: str
Expand Down Expand Up @@ -82,31 +96,31 @@ def __init__(
base_id: str,
table_name: str,
*,
timeout: Optional["pyairtable.api.api.TimeoutTuple"] = None,
retry_strategy: Optional[Retry] = None,
timeout: Optional["TimeoutTuple"] = None,
retry_strategy: Optional["Retry"] = None,
endpoint_url: str = "https://api.airtable.com",
): ...

@overload
def __init__(
self,
api_key: None,
base_id: "pyairtable.api.base.Base",
base_id: "Base",
table_name: str,
): ...

@overload
def __init__(
self,
api_key: None,
base_id: "pyairtable.api.base.Base",
base_id: "Base",
table_name: TableSchema,
): ...

def __init__(
self,
api_key: Union[None, str],
base_id: Union["pyairtable.api.base.Base", str],
base_id: Union["Base", str],
table_name: Union[str, TableSchema],
**kwargs: Any,
):
Expand Down Expand Up @@ -210,7 +224,7 @@ def id_or_name(self, quoted: bool = True) -> str:
return value

@property
def api(self) -> "pyairtable.api.api.Api":
def api(self) -> "Api":
"""
The API connection used by the table's :class:`~pyairtable.Base`.
"""
Expand Down Expand Up @@ -801,8 +815,3 @@ def upload_attachment(
}
response = self.api.post(url, json=payload)
return assert_typed_dict(UploadAttachmentResultDict, response)


# These are at the bottom of the module to avoid circular imports
import pyairtable.api.api # noqa
import pyairtable.api.base # noqa
19 changes: 9 additions & 10 deletions pyairtable/api/workspace.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
from functools import cached_property
from typing import Any, Dict, List, Optional, Sequence, Union
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Union

from pyairtable.models.schema import WorkspaceCollaborators
from pyairtable.utils import Url, UrlBuilder, cache_unless_forced, enterprise_only

if TYPE_CHECKING:
from pyairtable.api.api import Api
from pyairtable.api.base import Base


class Workspace:
"""
Expand Down Expand Up @@ -33,15 +37,15 @@ class _urls(UrlBuilder):

urls = cached_property(_urls)

def __init__(self, api: "pyairtable.api.api.Api", workspace_id: str):
def __init__(self, api: "Api", workspace_id: str):
self.api = api
self.id = workspace_id

def create_base(
self,
name: str,
tables: Sequence[Dict[str, Any]],
) -> "pyairtable.api.base.Base":
) -> "Base":
"""
Create a base in the given workspace.

Expand Down Expand Up @@ -73,7 +77,7 @@ def collaborators(self) -> WorkspaceCollaborators:
return WorkspaceCollaborators.from_api(payload, self.api, context=self)

@enterprise_only
def bases(self) -> List["pyairtable.api.base.Base"]:
def bases(self) -> List["Base"]:
"""
Retrieve all bases within the workspace.
"""
Expand Down Expand Up @@ -103,7 +107,7 @@ def delete(self) -> None:
@enterprise_only
def move_base(
self,
base: Union[str, "pyairtable.api.base.Base"],
base: Union[str, "Base"],
target: Union[str, "Workspace"],
index: Optional[int] = None,
) -> None:
Expand All @@ -123,8 +127,3 @@ def move_base(
if index is not None:
payload["targetIndex"] = index
self.api.post(self.urls.move_base, json=payload)


# These are at the bottom of the module to avoid circular imports
import pyairtable.api.api # noqa
import pyairtable.api.base # noqa
8 changes: 4 additions & 4 deletions pyairtable/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
documented separately, and none of its classes are exposed here.
"""

from .audit import AuditLogEvent, AuditLogResponse
from .collaborator import Collaborator
from .comment import Comment
from .webhook import Webhook, WebhookNotification, WebhookPayload
from pyairtable.models.audit import AuditLogEvent, AuditLogResponse
from pyairtable.models.collaborator import Collaborator
from pyairtable.models.comment import Comment
from pyairtable.models.webhook import Webhook, WebhookNotification, WebhookPayload

__all__ = [
"AuditLogResponse",
Expand Down
Loading