provide dependencies in constructor

This commit is contained in:
samschott 2022-05-23 21:10:54 +02:00
parent 4eda5d3dfe
commit a04005f8d4
5 changed files with 46 additions and 43 deletions

View File

@ -133,13 +133,14 @@ class DropboxClient:
def __init__(
self,
config_name: str,
cred_storage: CredentialStorage,
timeout: float = 100,
session: requests.Session | None = None,
) -> None:
self.config_name = config_name
self._auth_flow: DropboxOAuth2FlowNoRedirect | None = None
self.cred_storage = CredentialStorage(config_name)
self._cred_storage = cred_storage
self._state = MaestralState(config_name)
self._logger = scoped_logger(__name__, self.config_name)
@ -184,7 +185,7 @@ class DropboxClient:
:raises KeyringAccessError: if keyring access fails.
"""
return self.cred_storage.token is not None
return self._cred_storage.token is not None
def get_auth_url(self) -> str:
"""
@ -204,7 +205,7 @@ class DropboxClient:
def link(self, token: str) -> int:
"""
Links Maestral with a Dropbox account using the given access token. The token
will be stored for future usage as documented in the :mod:`oauth` module.
will be stored for future usage in the provided credential store.
:param token: OAuth token for Dropbox access.
:returns: 0 on success, 1 for an invalid token and 2 for connection errors.
@ -227,7 +228,7 @@ class DropboxClient:
except CONNECTION_ERRORS:
return 2
self.cred_storage.save_creds(
self._cred_storage.save_creds(
res.account_id, res.refresh_token, TokenType.Offline
)
self._auth_flow = None
@ -236,7 +237,8 @@ class DropboxClient:
def unlink(self) -> None:
"""
Unlinks the Dropbox account.
Unlinks the Dropbox account. The password will be deleted from the provided
credential storage.
:raises KeyringAccessError: if keyring access fails.
:raises DropboxAuthError: if we cannot authenticate with Dropbox.
@ -248,7 +250,7 @@ class DropboxClient:
with convert_api_errors():
self.dbx_base.auth_token_revoke()
self.cred_storage.delete_creds()
self._cred_storage.delete_creds()
def _init_sdk(
self, token: str | None = None, token_type: TokenType | None = None
@ -268,13 +270,13 @@ class DropboxClient:
if self._dbx:
return
if not (token or self.cred_storage.token):
if not (token or self._cred_storage.token):
raise NotLinkedError(
"No auth token set", "Please link a Dropbox account first."
)
token = token or self.cred_storage.token
token_type = token_type or self.cred_storage.token_type
token = token or self._cred_storage.token
token_type = token_type or self._cred_storage.token_type
if token_type is TokenType.Offline:
@ -349,6 +351,7 @@ class DropboxClient:
def clone(
self,
config_name: str | None = None,
cred_storage: CredentialStorage | None = None,
timeout: float | None = None,
session: requests.Session | None = None,
) -> DropboxClient:
@ -365,8 +368,9 @@ class DropboxClient:
config_name = config_name or self.config_name
timeout = timeout or self._timeout
session = session or self._session
cred_storage = cred_storage or self._cred_storage
client = self.__class__(config_name, timeout, session)
client = self.__class__(config_name, cred_storage, timeout, session)
if self._dbx:
client._dbx = self._dbx.clone(session=session)

View File

@ -28,6 +28,7 @@ from datetime import datetime, timezone
# local imports
from . import __version__
from .client import DropboxClient
from .keyring import CredentialStorage
from .core import (
SharedLinkMetadata,
FullAccount,
@ -38,7 +39,7 @@ from .core import (
LinkAccessLevel,
UpdateCheckResult,
)
from .sync import SyncDirection
from .sync import SyncDirection, SyncEngine
from .manager import SyncManager
from .models import SyncEvent, SyncErrorEntry
from .exceptions import (
@ -130,13 +131,13 @@ class Maestral:
def __init__(
self, config_name: str = "maestral", log_to_stderr: bool = False
) -> None:
self._config_name = validate_config_name(config_name)
self._conf = MaestralConfig(self._config_name)
self._state = MaestralState(self._config_name)
self._conf = MaestralConfig(self.config_name)
self._state = MaestralState(self.config_name)
self.cred_storage = CredentialStorage(self.config_name)
# Set up logging.
self._logger = scoped_logger(__name__, config_name)
self._logger = scoped_logger(__name__, self.config_name)
self._log_to_stderr = log_to_stderr
self._setup_logging()
@ -144,9 +145,9 @@ class Maestral:
self._check_and_run_post_update_scripts()
# Set up sync infrastructure.
self.client = DropboxClient(config_name=self.config_name)
self.manager = SyncManager(self.client)
self.sync = self.manager.sync
self.client = DropboxClient(self.config_name, self.cred_storage)
self.sync = SyncEngine(self.client)
self.manager = SyncManager(self.sync)
# Schedule background tasks.
self._loop = asyncio.get_event_loop_policy().get_event_loop()
@ -217,7 +218,7 @@ class Maestral:
self._logger.debug("Could not remove token from keyring", exc_info=True)
try:
self.client.cred_storage.delete_creds()
self.cred_storage.delete_creds()
except KeyringAccessError:
self._logger.debug("Could not remove token from keyring", exc_info=True)
@ -231,8 +232,8 @@ class Maestral:
def _setup_logging(self) -> None:
"""
Sets up logging to log files, status and error properties, desktop notifications,
the systemd journal if available, and to stderr if requested.
Sets up logging to log files, status and error properties, desktop
notifications, the systemd journal if available, and to stderr if requested.
"""
self._root_logger = scoped_logger("maestral", self.config_name)
(
@ -1537,7 +1538,7 @@ class Maestral:
while True:
if self.client.cred_storage.loaded:
if self.cred_storage.loaded:
# Only run if we have loaded the access token, we don't
# want to trigger any keyring access from here.

View File

@ -15,10 +15,13 @@ from threading import Event, RLock, Thread
from tempfile import TemporaryDirectory
from typing import Iterator, cast, TypeVar, Callable, Any, Generic
# external imports
from watchdog.observers import Observer
# local imports
from . import __url__
from . import notify
from .client import DropboxClient, API_HOST
from .client import API_HOST
from .core import TeamRootInfo, UserRootInfo
from .config import MaestralConfig, MaestralState, PersistentMutableSet
from .config.user import UserConfig
@ -41,7 +44,6 @@ from .exceptions import (
DropboxServerError,
)
from .sync import SyncEngine
from .fsevents import Observer
from .logging import scoped_logger
from .utils import removeprefix
from .utils.integration import check_connection, get_inotify_limits
@ -119,13 +121,11 @@ class SyncManager:
download_queue: PersistentQueue[str]
"""Queue of remote paths which have been newly included in syncing."""
def __init__(self, client: DropboxClient):
self.client = client
self.config_name = self.client.config_name
self._conf = MaestralConfig(self.config_name)
self._state = MaestralState(self.config_name)
self._logger = scoped_logger(__name__, self.config_name)
def __init__(self, sync: SyncEngine):
self.sync = sync
self._conf = MaestralConfig(self.sync.config_name)
self._state = MaestralState(self.sync.config_name)
self._logger = scoped_logger(__name__, self.sync.config_name)
self._lock = RLock()
@ -135,8 +135,6 @@ class SyncManager:
self.download_queue = PersistentQueue(self._state, "sync", "pending_downloads")
self.sync = SyncEngine(self.client)
self._startup_time = -1.0
self.connection_check_interval = 10
@ -449,8 +447,8 @@ class SyncManager:
self._logger.debug("Checking path root...")
account_info = self.client.get_account_info()
return self.client.namespace_id != account_info.root_info.root_namespace_id
account_info = self.sync.client.get_account_info()
return self.sync.client.namespace_id != account_info.root_info.root_namespace_id
def _update_path_root(self) -> None:
"""
@ -468,8 +466,8 @@ class SyncManager:
"Inconsistent namespace information found.",
)
root_info = self.client.account_info.root_info
team = self.client.account_info.team
root_info = self.sync.client.account_info.root_info
team = self.sync.client.account_info.team
team_name = team.name if team else "team"
@ -615,7 +613,7 @@ class SyncManager:
self.sync.excluded_items = new_excluded
# Update path root of client.
self.client.update_path_root(root_info)
self.sync.client.update_path_root(root_info)
# Trigger reindex.
self.sync.reset_sync_state()
@ -786,7 +784,7 @@ class SyncManager:
# Reload mignore rules.
self.sync.load_mignore_file()
self.client.get_space_usage()
self.sync.client.get_space_usage()
# Update path root and migrate local folders. This is required when a user
# joins or leaves a team and their root namespace changes.

View File

@ -62,7 +62,7 @@ def m(pytestconfig):
refresh_token = os.environ.get("DROPBOX_REFRESH_TOKEN")
token = access_token or refresh_token
token_type = TokenType.Legacy if access_token else TokenType.Offline
m.client.cred_storage.save_creds("1234", token, token_type)
m.cred_storage.save_creds("1234", token, token_type)
m.client.update_path_root()
# set local Dropbox directory
@ -116,7 +116,7 @@ def m(pytestconfig):
lock.release()
# remove creds from system keyring
m.client.cred_storage.delete_creds()
m.cred_storage.delete_creds()
# helper functions

View File

@ -11,8 +11,8 @@ from maestral.keyring import TokenType
def fake_linked(m: Maestral, account_info: FullAccount) -> None:
m.sync.client.get_account_info = mock.Mock(return_value=account_info) # type: ignore
m.sync.client.cred_storage.save_creds("account_id", "1234", TokenType.Offline)
m.client.get_account_info = mock.Mock(return_value=account_info) # type: ignore
m.cred_storage.save_creds("account_id", "1234", TokenType.Offline)
def verify_folder_structure(root: str, structure: dict) -> None: