From 51fc059d07cb5edad0e0d8ecb658588c37b88d53 Mon Sep 17 00:00:00 2001 From: Eduardo Garcia Ruiz Date: Mon, 7 Apr 2025 18:52:04 +0200 Subject: [PATCH 1/9] remove travis config file --- .travis.yml | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ffc81b4..0000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: python -python: - - "3.6" - - "3.7" - - "3.8" -# command to install dependencies -install: - - pip install '.[dev]' -# command to run tests -script: - - pytest --cov=./ --cov-report=html - -after_success: - - codecov From 9aa5bc1dbd8560b25803d63cc7edde97b1b9d78f Mon Sep 17 00:00:00 2001 From: Eduardo Garcia Ruiz Date: Mon, 7 Apr 2025 18:53:11 +0200 Subject: [PATCH 2/9] use pyproject.toml instead of setup.py --- pyproject.toml | 63 +++++++++++++++++++++++++++++++++++++++++++ requirements/dev.txt | 16 ----------- requirements/prod.txt | 10 ------- setup.py | 48 --------------------------------- 4 files changed, 63 insertions(+), 74 deletions(-) create mode 100644 pyproject.toml delete mode 100644 requirements/dev.txt delete mode 100644 requirements/prod.txt delete mode 100644 setup.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..29d182a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,63 @@ +[build-system] +requires = ["setuptools >= 77.0.3"] +build-backend = "setuptools.build_meta" + +[project] +name = "google-drive" +description = "Library and cli to manage and interact with your Google Drive" +readme = {file = "README.md", content-type = "text/markdown"} +version = "0.5.1" +requires-python = '>=3.9.21' +dependencies = [ + "google-api-core==1.31.5", + "google-api-python-client==2.166.0", + "google-auth==1.35.0", + "google-auth-httplib2==0.2.0", + "google-auth-oauthlib==0.4.1", + "googleapis-common-protos==1.56.0", + "dataclasses==0.6", + "click==8.1.7" +] +authors = [ + {name = "Eduardo Garcia", email = "garciaruiz.edu+maintain+google-drive-python@gmail.com"}, +] +maintainers = [ + {name = "Eduardo Garcia", email = "garciaruiz.edu+maintain+google-drive-python@gmail.com"}, +] +license = "Apache-2.0" +license-files = [ + "LICENSE*", + "setuptools/_vendor/LICENSE*", +] +keywords = ["google", "drive", "cli"] + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Topic :: Software Development :: Libraries", +] + +[project.optional-dependencies] +dev = [ + "pylint==2.16.0b1", + "autopep8==2.0.1", + "pytest==8.3.5", + "pytest-cov==6.1.1", + "ipython==7.23.1", + "twine==6.1.0" +] + +[project.scripts] +google-drive = "googledrive.cli:googledrive" + +[project.entry-points."console_scripts"] +google-drive = "googledrive.cli:googledrive" + +[project.urls] +Homepage = "https://github.com/eduardogr/google-drive-python" +Documentation = "https://github.com/eduardogr/google-drive-python" +Repository = "https://github.com/eduardogr/google-drive-python.git" +Issues = "https://github.com/eduardogr/google-drive-python/issues" +Changelog = "https://github.com/eduardogr/google-drive-python/blob/main/CHANGELOG.md" + +[tool.setuptools.packages.find] +where = ["googledrive"] \ No newline at end of file diff --git a/requirements/dev.txt b/requirements/dev.txt deleted file mode 100644 index 9ecf8ba..0000000 --- a/requirements/dev.txt +++ /dev/null @@ -1,16 +0,0 @@ -# linter -pylint==2.16.0b1 - -# format python code to PEP 8 stlye guide -autopep8==2.0.1 - -# test runner -pytest==7.2.1 -pytest-cov==4.0.0 - -# upgraded Python shell for dev env -ipython==7.23.1 - -codecov==2.1.12 - -twine==4.0.2 \ No newline at end of file diff --git a/requirements/prod.txt b/requirements/prod.txt deleted file mode 100644 index 1a191c9..0000000 --- a/requirements/prod.txt +++ /dev/null @@ -1,10 +0,0 @@ -google-api-core==1.31.5 -google-api-python-client==2.6.0 -google-auth==1.35.0 -google-auth-httplib2==0.1.0 -google-auth-oauthlib==0.4.1 -googleapis-common-protos==1.56.0 - -dataclasses==0.6 - -click==8.1.7 \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index cd09559..0000000 --- a/setup.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import pathlib - -import pkg_resources -from setuptools import setup - - -def read(fname): - this_directory = pathlib.Path(__file__).parent - long_description = (this_directory / fname).read_text() - return long_description - - -def read_requirements(path): - with pathlib.Path(path).open() as requirements_txt: - return [ - str(requirement) - for requirement in pkg_resources.parse_requirements(requirements_txt) - ] - - -requirements = read_requirements("requirements/prod.txt") -extra_requirements_dev = read_requirements("requirements/dev.txt") - - -setup( - name="google-drive", - version="0.5.1", - author="Eduardo Garcia", - author_email="garciaruiz.edu+maintain+google-drive-python@gmail.com", - maintainer="Eduardo GarcĂ­a", - maintainer_email="garciaruiz.edu+google-drive-python@gmail.com", - description=("Library and cli to manage and interact with your Google Drive"), - license="Apache", - keywords="google drive", - url="https://github.com/eduardogr/google-drive-python", - packages=["googledrive"], - install_requires=requirements, - extras_require={"dev": extra_requirements_dev}, - long_description=read("README.md"), - long_description_content_type="text/markdown", - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Topic :: Software Development :: Libraries", - "License :: OSI Approved :: Apache Software License", - ], - entry_points={"console_scripts": ["google-drive = googledrive.cli:googledrive"]}, -) From e3dcfac3ec06340dc4cfa07f5b04b11ce4701a08 Mon Sep 17 00:00:00 2001 From: Eduardo Garcia Ruiz Date: Mon, 7 Apr 2025 18:53:45 +0200 Subject: [PATCH 3/9] use new way of importing python packages based on PEP --- googledrive/api.py | 69 ++++++++++++------------- googledrive/cli.py | 28 +++++------ googledrive/exceptions.py | 4 +- googledrive/mappers.py | 4 +- tests/test_api.py | 103 ++++++++++++++++++-------------------- tests/test_mappers.py | 10 ++-- 6 files changed, 104 insertions(+), 114 deletions(-) diff --git a/googledrive/api.py b/googledrive/api.py index ed53384..f40f046 100644 --- a/googledrive/api.py +++ b/googledrive/api.py @@ -1,19 +1,14 @@ import pickle import os.path -import json from googleapiclient.discovery import build from googleapiclient.errors import HttpError from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request -from googledrive.models import GoogleApiClientHttpError -from googledrive.models import GoogleApiClientHttpErrorBuilder -from googledrive.mappers import GoogleFileDictToGoogleFile -from googledrive.exceptions import MissingGoogleDriveFolderException -from googledrive.exceptions import MissingGoogleDriveFileException -from googledrive.exceptions import GoogleApiClientHttpErrorException - +from googledrive import models +from googledrive import mappers +from googledrive import exceptions class GoogleAuth: def authenticate(self, credentials, scopes): @@ -131,10 +126,10 @@ def create_folder(self, name): .execute() ) except HttpError as e: - http_error = GoogleApiClientHttpErrorBuilder().from_http_error(e) - raise GoogleApiClientHttpErrorException(http_error) + http_error = models.GoogleApiClientHttpErrorBuilder().from_http_error(e) + raise exceptions.GoogleApiClientHttpErrorException(http_error) - return GoogleFileDictToGoogleFile().google_file_dict_to_google_file(folder) + return mappers.GoogleFileDictToGoogleFile().google_file_dict_to_google_file(folder) def create_file(self, name, mimetype): splitted_path = list(self.__split_path(name)) @@ -145,7 +140,7 @@ def create_file(self, name, mimetype): parent_name = splitted_path[-2] parent_folder = self.get_folder(parent_name) if parent_folder == None: - raise MissingGoogleDriveFolderException( + raise exceptions.MissingGoogleDriveFolderException( "Missing folder: {}".format(parent_name) ) parents = [parent_folder.id] @@ -161,10 +156,10 @@ def create_file(self, name, mimetype): .execute() ) except HttpError as e: - http_error = GoogleApiClientHttpErrorBuilder().from_http_error(e) - raise GoogleApiClientHttpErrorException(http_error) + http_error = models.GoogleApiClientHttpErrorBuilder().from_http_error(e) + raise exceptions.GoogleApiClientHttpErrorException(http_error) - return GoogleFileDictToGoogleFile().google_file_dict_to_google_file(file) + return mappers.GoogleFileDictToGoogleFile().google_file_dict_to_google_file(file) def update_file_parent(self, file_id, current_parent, new_parent): drive_service = super().get_service( @@ -176,8 +171,8 @@ def update_file_parent(self, file_id, current_parent, new_parent): ) file_update.execute() except HttpError as e: - http_error = GoogleApiClientHttpErrorBuilder().from_http_error(e) - raise GoogleApiClientHttpErrorException(http_error) + http_error = models.GoogleApiClientHttpErrorBuilder().from_http_error(e) + raise exceptions.GoogleApiClientHttpErrorException(http_error) def get_file_from_id(self, file_id: str): drive_service = super().get_service( @@ -190,12 +185,12 @@ def get_file_from_id(self, file_id: str): .execute() ) - return GoogleFileDictToGoogleFile().google_file_dict_to_google_file( + return mappers.GoogleFileDictToGoogleFile().google_file_dict_to_google_file( google_file_dict ) except HttpError as e: - http_error = GoogleApiClientHttpErrorBuilder().from_http_error(e) - raise GoogleApiClientHttpErrorException(http_error) + http_error = models.GoogleApiClientHttpErrorBuilder().from_http_error(e) + raise exceptions.GoogleApiClientHttpErrorException(http_error) def list_files(self, page_token: str, query: str): drive_service = super().get_service( @@ -215,11 +210,11 @@ def list_files(self, page_token: str, query: str): .execute() ) except HttpError as e: - http_error = GoogleApiClientHttpErrorBuilder().from_http_error(e) - raise GoogleApiClientHttpErrorException(http_error) + http_error = models.GoogleApiClientHttpErrorBuilder().from_http_error(e) + raise exceptions.GoogleApiClientHttpErrorException(http_error) google_files = [ - GoogleFileDictToGoogleFile().google_file_dict_to_google_file( + mappers.GoogleFileDictToGoogleFile().google_file_dict_to_google_file( google_file_dict ) for google_file_dict in response.get("files", []) @@ -243,8 +238,8 @@ def copy_file(self, file_id, new_filename): ) return results.get("id") except HttpError as e: - http_error = GoogleApiClientHttpErrorBuilder().from_http_error(e) - raise GoogleApiClientHttpErrorException(http_error) + http_error = models.GoogleApiClientHttpErrorBuilder().from_http_error(e) + raise exceptions.GoogleApiClientHttpErrorException(http_error) def create_permission(self, document_id: str, role: str, email_address): drive_service = super().get_service( @@ -256,8 +251,8 @@ def create_permission(self, document_id: str, role: str, email_address): body={"type": "user", "emailAddress": email_address, "role": role}, ).execute() except HttpError as e: - http_error = GoogleApiClientHttpErrorBuilder().from_http_error(e) - raise GoogleApiClientHttpErrorException(http_error) + http_error = models.GoogleApiClientHttpErrorBuilder().from_http_error(e) + raise exceptions.GoogleApiClientHttpErrorException(http_error) # # High level API access @@ -282,7 +277,7 @@ def googledrive_ls(self, path: str): else: folder = self.get_folder(splitted_path[0]) if folder is None: - raise MissingGoogleDriveFolderException( + raise exceptions.MissingGoogleDriveFolderException( "Missing folder: {}".format(splitted_path[0]) ) @@ -291,7 +286,7 @@ def googledrive_ls(self, path: str): folder = self.__get_file(query, path_element) if folder is None: - raise MissingGoogleDriveFolderException( + raise exceptions.MissingGoogleDriveFolderException( "Missing folder: {}".format(path_element) ) @@ -315,7 +310,7 @@ def googledrive_get_file(self, path: str): folder = self.get_folder(splitted_path[0]) if folder is None: - raise MissingGoogleDriveFolderException( + raise exceptions.MissingGoogleDriveFolderException( "Missing folder: {}".format(path[0]) ) @@ -327,7 +322,7 @@ def googledrive_get_file(self, path: str): folder = self.__get_file(query, path_element) if folder is None: - raise MissingGoogleDriveFolderException( + raise exceptions.MissingGoogleDriveFolderException( "Missing folder: {}".format(path_element) ) @@ -365,8 +360,8 @@ def __get_files(self, query: str): page_token = next_page_token return None except HttpError as e: - http_error = GoogleApiClientHttpErrorBuilder().from_http_error(e) - raise GoogleApiClientHttpErrorException(http_error) + http_error = models.GoogleApiClientHttpErrorBuilder().from_http_error(e) + raise exceptions.GoogleApiClientHttpErrorException(http_error) class SheetsService(GoogleService): @@ -396,7 +391,7 @@ def create_spreadsheet(self, filename): # def get_file_values(self, spreadsheet_id, rows_range): if spreadsheet_id is None: - raise MissingGoogleDriveFileException( + raise exceptions.MissingGoogleDriveFileException( "Missing file: {}".format(spreadsheet_id) ) @@ -422,8 +417,8 @@ def get_file_values(self, spreadsheet_id, rows_range): self.cached_file_values.update({spreadsheet_id: {rows_range: values}}) return values except HttpError as e: - http_error = GoogleApiClientHttpErrorBuilder().from_http_error(e) - raise GoogleApiClientHttpErrorException(http_error) + http_error = models.GoogleApiClientHttpErrorBuilder().from_http_error(e) + raise exceptions.GoogleApiClientHttpErrorException(http_error) def update_file_values( self, spreadsheet_id, rows_range, value_input_option, values @@ -506,7 +501,7 @@ def get_file_rows_from_folder( google_file = super().googledrive_get_file(file_path) if google_file is None: - raise MissingGoogleDriveFileException("Missing file: {}".format(filename)) + raise exceptions.MissingGoogleDriveFileException("Missing file: {}".format(filename)) values = super().get_file_values(google_file.id, rows_range) diff --git a/googledrive/cli.py b/googledrive/cli.py index 9a98582..3518a01 100644 --- a/googledrive/cli.py +++ b/googledrive/cli.py @@ -1,9 +1,7 @@ -import sys import click -from googledrive.api import GoogleAuth -from googledrive.api import GoogleDrive -from googledrive.exceptions import GoogleApiClientHttpErrorException +from googledrive import api +from googledrive import exceptions class Config: @@ -24,7 +22,7 @@ class Config: @click.argument("credentials", envvar="CREDENTIALS", type=click.Path(exists=True)) def login(credentials): """Perform a login with google oauth""" - GoogleAuth().authenticate(credentials=credentials, scopes=Config.SCOPES) + api.GoogleAuth().authenticate(credentials=credentials, scopes=Config.SCOPES) @click.command() @@ -33,9 +31,9 @@ def login(credentials): def ls(credentials, path): """List directory contents""" try: - google_drive = GoogleDrive(credentials, Config.SCOPES) + google_drive = api.GoogleDrive(credentials, Config.SCOPES) files = google_drive.googledrive_ls(path) - except GoogleApiClientHttpErrorException as e: + except exceptions.GoogleApiClientHttpErrorException as e: error = e.get_google_api_client_http_error() print(f"An http exception occured requesting google's API:\n") print(f" - Code: {error.code}") @@ -55,9 +53,9 @@ def ls(credentials, path): def get(id, credentials): """Get file metadata""" try: - google_drive = GoogleDrive(credentials, Config.SCOPES) + google_drive = api.GoogleDrive(credentials, Config.SCOPES) google_file = google_drive.get_file_from_id(id) - except GoogleApiClientHttpErrorException as e: + except exceptions.GoogleApiClientHttpErrorException as e: error = e.get_google_api_client_http_error() print(f"An http exception occured requesting google's API:\n") print(f" - Code: {error.code}") @@ -84,9 +82,9 @@ def get(id, credentials): def mkdir(credentials, name): """Make directory""" try: - google_drive = GoogleDrive(credentials, Config.SCOPES) + google_drive = api.GoogleDrive(credentials, Config.SCOPES) folder = google_drive.create_folder(name) - except GoogleApiClientHttpErrorException as e: + except exceptions.GoogleApiClientHttpErrorException as e: error = e.get_google_api_client_http_error() print(f"An http exception occured requesting google's API:\n") print(f" - Code: {error.code}") @@ -104,9 +102,9 @@ def mkdir(credentials, name): def get_mimetypes(credentials): """Get Mimetypes availables in this API implementation""" try: - google_drive = GoogleDrive(credentials, Config.SCOPES) + google_drive = api.GoogleDrive(credentials, Config.SCOPES) mimetypes = google_drive.get_mymetypes() - except GoogleApiClientHttpErrorException as e: + except exceptions.GoogleApiClientHttpErrorException as e: error = e.get_google_api_client_http_error() print(f"An http exception occured requesting google's API:\n") print(f" - Code: {error.code}") @@ -127,9 +125,9 @@ def get_mimetypes(credentials): def touch(credentials, mymetype, name): """Create empty file of specified mimetype""" try: - google_drive = GoogleDrive(credentials, Config.SCOPES) + google_drive = api.GoogleDrive(credentials, Config.SCOPES) file = google_drive.create_file(name=name, mimetype=mymetype) - except GoogleApiClientHttpErrorException as e: + except exceptions.GoogleApiClientHttpErrorException as e: error = e.get_google_api_client_http_error() print(f"An http exception occured requesting google's API:\n") print(f" - Code: {error.code}") diff --git a/googledrive/exceptions.py b/googledrive/exceptions.py index 054b196..5b0c021 100644 --- a/googledrive/exceptions.py +++ b/googledrive/exceptions.py @@ -1,4 +1,4 @@ -from googledrive.models import GoogleApiClientHttpError +from googledrive import models class CustomException(Exception): @@ -16,7 +16,7 @@ def get_str(self, class_name): class GoogleApiClientHttpErrorException(Exception): - def __init__(self, google_api_client_http_error: GoogleApiClientHttpError): + def __init__(self, google_api_client_http_error: models.GoogleApiClientHttpError): self.google_api_client_http_error = google_api_client_http_error def get_google_api_client_http_error(self): diff --git a/googledrive/mappers.py b/googledrive/mappers.py index c4c34e6..22c7669 100644 --- a/googledrive/mappers.py +++ b/googledrive/mappers.py @@ -1,4 +1,4 @@ -from googledrive.models import GoogleFile +from googledrive import models class GoogleFileDictToGoogleFile: @@ -12,7 +12,7 @@ def google_file_dict_to_google_file(self, google_file_dict): mime_type = google_file_dict.get("mimeType") or "" export_links = google_file_dict.get("exportLinks") or {} - return GoogleFile( + return models.GoogleFile( name=name, id=id, parents=parents, diff --git a/tests/test_api.py b/tests/test_api.py index 9cef113..e0dfb69 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,11 +1,8 @@ from unittest import TestCase -from googledrive.api import FilesAPI, GoogleDrive -from googledrive.api import SheetsService, DocsService -from googledrive.models import GoogleFile -from googledrive.exceptions import MissingGoogleDriveFolderException -from googledrive.exceptions import MissingGoogleDriveFileException -from googledrive.exceptions import GoogleApiClientHttpErrorException +from googledrive import api +from googledrive import models +from googledrive import exceptions from tests.common.mocks import RawSheetsServiceMock from tests.common.mocks import RawDocsServiceMock @@ -17,28 +14,28 @@ from tests.common.mocks import MockDocsService -class DocServiceSut(DocsService, MockGoogleService): +class DocServiceSut(api.DocsService, MockGoogleService): "Inject a mock into the DocsService dependency" def __init__(self): super().__init__("", []) -class SheetsServiceSut(SheetsService, MockGoogleService): +class SheetsServiceSut(api.SheetsService, MockGoogleService): "Inject a mock into the SheetsService dependency" def __init__(self): super().__init__("", []) -class GoogleDriveSut(GoogleDrive, MockGoogleService): +class GoogleDriveSut(api.GoogleDrive, MockGoogleService): "Inject a mock into the GoogleDrive dependency" def __init__(self): super().__init__("", []) -class FilesAPISut(FilesAPI, MockGoogleDrive, MockSheetsService, MockDocsService): +class FilesAPISut(api.FilesAPI, MockGoogleDrive, MockSheetsService, MockDocsService): "Inject mocks into FilesAPI dependencies" @@ -51,8 +48,8 @@ def setUp(self): drive_service = RawGoogleServiceMock(raw_google_service_files) self.sut.set_service( - GoogleDrive.DRIVE_SERVICE_ID, - GoogleDrive.DRIVE_SERVICE_VERSION, + api.GoogleDrive.DRIVE_SERVICE_ID, + api.GoogleDrive.DRIVE_SERVICE_VERSION, drive_service, ) @@ -63,7 +60,7 @@ def test_create_folder_ok(self): def test_create_file_when_no_parent_ok(self): path = "filename" - mimetype = GoogleDrive.MIMETYPE_DOCUMENT + mimetype = api.GoogleDrive.MIMETYPE_DOCUMENT self.sut.create_file(path, mimetype) @@ -71,14 +68,14 @@ def test_create_file_when_parent_ok(self): # given: parent_folder = "parentfolder" path = f"{parent_folder}/filename" - mimetype = GoogleDrive.MIMETYPE_DOCUMENT + mimetype = api.GoogleDrive.MIMETYPE_DOCUMENT files = [{"name": parent_folder, "id": parent_folder, "parents": []}] - files_by_query = {GoogleDrive.QUERY_IS_FOLDER: RawGoogleListMock(files)} + files_by_query = {api.GoogleDrive.QUERY_IS_FOLDER: RawGoogleListMock(files)} raw_google_service_files = RawGoogleServiceFilesMock(files_by_query) drive_service = RawGoogleServiceMock(raw_google_service_files) self.sut.set_service( - GoogleDrive.DRIVE_SERVICE_ID, - GoogleDrive.DRIVE_SERVICE_VERSION, + api.GoogleDrive.DRIVE_SERVICE_ID, + api.GoogleDrive.DRIVE_SERVICE_VERSION, drive_service, ) @@ -87,7 +84,7 @@ def test_create_file_when_parent_ok(self): def test_googledrive_ls_when_folder_no_exists(self): # when: - with self.assertRaises(MissingGoogleDriveFolderException): + with self.assertRaises(exceptions.MissingGoogleDriveFolderException): self.sut.googledrive_ls("/unexistent") def test_googledrive_ls_when_for(self): @@ -99,7 +96,7 @@ def test_googledrive_ls_when_for(self): ] files_by_query = { "mimeType='application/vnd.google-apps.folder'": RawGoogleListMock(files), - f"{GoogleDrive.QUERY_IS_FILE} and 'basefolder' in parents": RawGoogleListMock( + f"{api.GoogleDrive.QUERY_IS_FILE} and 'basefolder' in parents": RawGoogleListMock( listed_files ), } @@ -107,8 +104,8 @@ def test_googledrive_ls_when_for(self): drive_service = RawGoogleServiceMock(raw_google_service_files) self.sut.set_service( - GoogleDrive.DRIVE_SERVICE_ID, - GoogleDrive.DRIVE_SERVICE_VERSION, + api.GoogleDrive.DRIVE_SERVICE_ID, + api.GoogleDrive.DRIVE_SERVICE_VERSION, drive_service, ) @@ -130,13 +127,13 @@ def test_googledrive_ls_when_for_none(self): drive_service = RawGoogleServiceMock(raw_google_service_files) self.sut.set_service( - GoogleDrive.DRIVE_SERVICE_ID, - GoogleDrive.DRIVE_SERVICE_VERSION, + api.GoogleDrive.DRIVE_SERVICE_ID, + api.GoogleDrive.DRIVE_SERVICE_VERSION, drive_service, ) # when: - with self.assertRaises(MissingGoogleDriveFolderException): + with self.assertRaises(exceptions.MissingGoogleDriveFolderException): self.sut.googledrive_ls("/something/unexistent") def test_googledrive_ls_when_no_for(self): @@ -145,13 +142,13 @@ def test_googledrive_ls_when_no_for(self): {"name": "file_1", "id": "file_1", "parents": []}, {"name": "file_2", "id": "file_2", "parents": []}, ] - files_by_query = {GoogleDrive.QUERY_IS_FILE: RawGoogleListMock(listed_files)} + files_by_query = {api.GoogleDrive.QUERY_IS_FILE: RawGoogleListMock(listed_files)} raw_google_service_files = RawGoogleServiceFilesMock(files_by_query) drive_service = RawGoogleServiceMock(raw_google_service_files) self.sut.set_service( - GoogleDrive.DRIVE_SERVICE_ID, - GoogleDrive.DRIVE_SERVICE_VERSION, + api.GoogleDrive.DRIVE_SERVICE_ID, + api.GoogleDrive.DRIVE_SERVICE_VERSION, drive_service, ) @@ -170,7 +167,7 @@ def test_googledrive_get_file_when_incorrect_path(self): def test_googledrive_get_file_when_unexistent_file(self): # when: - with self.assertRaises(MissingGoogleDriveFolderException): + with self.assertRaises(exceptions.MissingGoogleDriveFolderException): self.sut.googledrive_get_file("/unexistent/path/file") def test_googledrive_get_file_for(self): @@ -184,11 +181,11 @@ def test_googledrive_get_file_for(self): {"name": "existent_file", "id": "existent_file", "parents": ["path"]}, ] files_by_query = { - GoogleDrive.QUERY_IS_FOLDER: RawGoogleListMock(files), - f"{GoogleDrive.QUERY_IS_FOLDER} and 'base' in parents": RawGoogleListMock( + api.GoogleDrive.QUERY_IS_FOLDER: RawGoogleListMock(files), + f"{api.GoogleDrive.QUERY_IS_FOLDER} and 'base' in parents": RawGoogleListMock( files ), - f"{GoogleDrive.QUERY_IS_FILE} and 'path' in parents": RawGoogleListMock( + f"{api.GoogleDrive.QUERY_IS_FILE} and 'path' in parents": RawGoogleListMock( listed_files ), } @@ -196,8 +193,8 @@ def test_googledrive_get_file_for(self): drive_service = RawGoogleServiceMock(raw_google_service_files) self.sut.set_service( - GoogleDrive.DRIVE_SERVICE_ID, - GoogleDrive.DRIVE_SERVICE_VERSION, + api.GoogleDrive.DRIVE_SERVICE_ID, + api.GoogleDrive.DRIVE_SERVICE_VERSION, drive_service, ) @@ -210,20 +207,20 @@ def test_googledrive_get_file_for_none(self): # given: files = [{"name": "base", "id": "base", "parents": []}] files_by_query = { - GoogleDrive.QUERY_IS_FOLDER: RawGoogleListMock(files), - f"{GoogleDrive.QUERY_IS_FOLDER} and 'base' in parents": RawGoogleListMock(), + api.GoogleDrive.QUERY_IS_FOLDER: RawGoogleListMock(files), + f"{api.GoogleDrive.QUERY_IS_FOLDER} and 'base' in parents": RawGoogleListMock(), } raw_google_service_files = RawGoogleServiceFilesMock(files_by_query) drive_service = RawGoogleServiceMock(raw_google_service_files) self.sut.set_service( - GoogleDrive.DRIVE_SERVICE_ID, - GoogleDrive.DRIVE_SERVICE_VERSION, + api.GoogleDrive.DRIVE_SERVICE_ID, + api.GoogleDrive.DRIVE_SERVICE_VERSION, drive_service, ) # when: - with self.assertRaises(MissingGoogleDriveFolderException): + with self.assertRaises(exceptions.MissingGoogleDriveFolderException): self.sut.googledrive_get_file("/base/unexistent/unexistent_file") def test_googledrive_get_file_no_for(self): @@ -234,8 +231,8 @@ def test_googledrive_get_file_no_for(self): {"name": "existent_file", "id": "existent_file", "parents": ["path"]}, ] files_by_query = { - GoogleDrive.QUERY_IS_FOLDER: RawGoogleListMock(files), - f"{GoogleDrive.QUERY_IS_FILE} and 'base' in parents": RawGoogleListMock( + api.GoogleDrive.QUERY_IS_FOLDER: RawGoogleListMock(files), + f"{api.GoogleDrive.QUERY_IS_FILE} and 'base' in parents": RawGoogleListMock( listed_files ), } @@ -243,8 +240,8 @@ def test_googledrive_get_file_no_for(self): drive_service = RawGoogleServiceMock(raw_google_service_files) self.sut.set_service( - GoogleDrive.DRIVE_SERVICE_ID, - GoogleDrive.DRIVE_SERVICE_VERSION, + api.GoogleDrive.DRIVE_SERVICE_ID, + api.GoogleDrive.DRIVE_SERVICE_VERSION, drive_service, ) @@ -284,8 +281,8 @@ class TestSheetsService(TestCase): def setUp(self): self.sut = SheetsServiceSut() self.sut.set_service( - SheetsService.SHEETS_SERVICE_ID, - SheetsService.SHEETS_SERVICE_VERSION, + api.SheetsService.SHEETS_SERVICE_ID, + api.SheetsService.SHEETS_SERVICE_VERSION, RawSheetsServiceMock(), ) @@ -319,8 +316,8 @@ class TestDocsService(TestCase): def setUp(self): self.sut = DocServiceSut() self.sut.set_service( - DocsService.DOCS_SERVICE_ID, - DocsService.DOCS_SERVICE_VERSION, + api.DocsService.DOCS_SERVICE_ID, + api.DocsService.DOCS_SERVICE_VERSION, RawDocsServiceMock(), ) @@ -365,7 +362,7 @@ def test_create_spreadsheet(self): # given: folder_parent = "parent" filename = "filename" - folder = GoogleFile( + folder = models.GoogleFile( id="new_parent_id", name="folder", parents=[], mime_type="", export_links={} ) @@ -411,7 +408,7 @@ def test_correct_number_executions_when_get_folder_and_exists(self): self.sut.set_pages_requested(1) self.sut.set_response_files( [ - GoogleFile( + models.GoogleFile( id="some id", name=folder_name, parents=[], @@ -443,7 +440,7 @@ def test_correct_query_when_get_folder(self): self.assertEqual(1, len(calls)) self.assertIn("list_files", calls) - self.assertEqual(GoogleDrive.QUERY_IS_FOLDER, calls["list_files"][0]["query"]) + self.assertEqual(api.GoogleDrive.QUERY_IS_FOLDER, calls["list_files"][0]["query"]) def test_get_file_rows_from_folder(self): # given: @@ -452,7 +449,7 @@ def test_get_file_rows_from_folder(self): file_id = "file_id" self.sut.set_googledrive_get_file_response( f"/{foldername}/{filename}", - GoogleFile( + models.GoogleFile( id=file_id, name=filename, parents=[], mime_type="", export_links={} ), ) @@ -481,7 +478,7 @@ def test_get_file_rows_from_folder_when_missing_google_folder_exception(self): self.sut.set_googledrive_get_file_raise_exception(f"/{foldername}/{filename}") # when: - with self.assertRaises(MissingGoogleDriveFolderException): + with self.assertRaises(exceptions.MissingGoogleDriveFolderException): self.sut.get_file_rows_from_folder(foldername, filename, rows_range) # then: @@ -498,7 +495,7 @@ def test_get_file_rows_from_folder_when_missing_google_file_exception(self): rows_range = "A2::F4" # when: - with self.assertRaises(MissingGoogleDriveFileException): + with self.assertRaises(exceptions.MissingGoogleDriveFileException): self.sut.get_file_rows_from_folder(foldername, filename, rows_range) # then: @@ -528,7 +525,7 @@ def test_get_file_rows_when_http_error(self): self.sut.raise_exception_for_get_file_values_for_ids([file_id]) # when: - with self.assertRaises(GoogleApiClientHttpErrorException): + with self.assertRaises(exceptions.GoogleApiClientHttpErrorException): self.sut.get_file_values(file_id, rows_range) # then: diff --git a/tests/test_mappers.py b/tests/test_mappers.py index 3ec8f61..db2b288 100644 --- a/tests/test_mappers.py +++ b/tests/test_mappers.py @@ -1,17 +1,17 @@ from unittest import TestCase -from googledrive.mappers import GoogleFileDictToGoogleFile -from googledrive.models import GoogleFile +from googledrive import mappers +from googledrive import models class TestGoogleFileDictToGoogleFile(TestCase): def test_to_json(self): # given: google_file_dict = {"name": "id", "id": "name", "parents": "[]"} - expected_google_file = GoogleFile( + expected_google_file = models.GoogleFile( id="id", name="name", parents=[], mime_type="", export_links={} ) - sut = GoogleFileDictToGoogleFile() + sut = mappers.GoogleFileDictToGoogleFile() # when: json_dict = sut.google_file_dict_to_google_file(google_file_dict) @@ -23,7 +23,7 @@ def test_to_json_when_is_none(self): # given: google_file_dict = None expected_google_file = None - sut = GoogleFileDictToGoogleFile() + sut = mappers.GoogleFileDictToGoogleFile() # when: json_dict = sut.google_file_dict_to_google_file(google_file_dict) From fc0e6ee24e5d7456f2fbf7e4aa0bb8893c5773a2 Mon Sep 17 00:00:00 2001 From: Eduardo Garcia Ruiz Date: Mon, 7 Apr 2025 18:58:33 +0200 Subject: [PATCH 4/9] add github action to test simple workflow --- .github/workflows/python-package.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/python-package.yml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..c8422ee --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,21 @@ +name: Python package + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + # You can test your matrix by printing the current Python version + - name: Display Python version + run: python -c "import sys; print(sys.version)" From 8ea7a56b7d43ad5a488c6a6d2b7015828d2bcd4a Mon Sep 17 00:00:00 2001 From: Eduardo Garcia Ruiz Date: Mon, 7 Apr 2025 19:06:44 +0200 Subject: [PATCH 5/9] modify gh action to run tests --- .github/workflows/python-package.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index c8422ee..78514df 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -12,10 +12,15 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - # You can test your matrix by printing the current Python version - - name: Display Python version - run: python -c "import sys; print(sys.version)" + cache: 'pip' + + - name: Installing requirements + run: pip install -e '.[dev]'' + + - name: Running tests + run: pytest From 79c4d9860b230b8abda38be7bd7485d9b659d07c Mon Sep 17 00:00:00 2001 From: Eduardo Garcia Ruiz Date: Mon, 7 Apr 2025 19:07:41 +0200 Subject: [PATCH 6/9] modify gh action; fixing typo --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 78514df..7d13a78 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -20,7 +20,7 @@ jobs: cache: 'pip' - name: Installing requirements - run: pip install -e '.[dev]'' + run: pip install -e '.[dev]' - name: Running tests run: pytest From 61b8c3796fb161d8af995845aa8db70ace644121 Mon Sep 17 00:00:00 2001 From: Eduardo Garcia Ruiz Date: Mon, 7 Apr 2025 19:15:07 +0200 Subject: [PATCH 7/9] removing unused docs/ directory --- docs/Makefile | 20 ---------------- docs/make.bat | 35 ---------------------------- docs/source/conf.py | 54 ------------------------------------------- docs/source/index.rst | 20 ---------------- 4 files changed, 129 deletions(-) delete mode 100644 docs/Makefile delete mode 100644 docs/make.bat delete mode 100644 docs/source/conf.py delete mode 100644 docs/source/index.rst diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index d0c3cbf..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 6247f7e..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index adc79d7..0000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,54 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = "google-drive" -copyright = "2020, Eduardo" -author = "Eduardo" - -# The full version, including alpha/beta/rc tags -release = "0.3.2" - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "alabaster" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] diff --git a/docs/source/index.rst b/docs/source/index.rst deleted file mode 100644 index db03e55..0000000 --- a/docs/source/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. google-drive documentation master file, created by - sphinx-quickstart on Sun Nov 22 22:18:43 2020. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to google-drive's documentation! -======================================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` From 595558f6d3a94dde0d93a99324f5d641fe7c050b Mon Sep 17 00:00:00 2001 From: Eduardo Garcia Ruiz Date: Mon, 7 Apr 2025 19:20:20 +0200 Subject: [PATCH 8/9] Update README; changing badges for build status and python version --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a6022f6..871003a 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,8 @@ Library and cli to manage and interact with your Google Drive, sheets and docs 1. [License](#license) # Introduction -[![Build Status](https://travis-ci.org/eduardogr/google-drive-python.svg?branch=main)](https://travis-ci.org/github/eduardogr/google-drive-python) -[![codecov](https://codecov.io/gh/eduardogr/google-drive-python/branch/main/graph/badge.svg?token=E183Y3LLXX)](https://codecov.io/gh/eduardogr/google-drive-python) -[![Python](https://img.shields.io/badge/Python-v3.6%2B-blue)]() +![Build Status](https://github.com/eduardogr/google-drive-python/actions/workflows/python-package.yml/badge.svg?event=push) +[![Python](https://img.shields.io/badge/Python-v3.9%2B-blue)]() [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) [![GitHub license](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/eduardogr/google-drive-python/blob/main/LICENSE) From 143a215e6d1ff0e26124f9452d829373caf87ad8 Mon Sep 17 00:00:00 2001 From: Eduardo Garcia Ruiz Date: Mon, 7 Apr 2025 19:21:59 +0200 Subject: [PATCH 9/9] changing name of gh action file --- .github/workflows/{python-package.yml => python-tests.yml} | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{python-package.yml => python-tests.yml} (96%) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-tests.yml similarity index 96% rename from .github/workflows/python-package.yml rename to .github/workflows/python-tests.yml index 7d13a78..474ac79 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-tests.yml @@ -1,4 +1,4 @@ -name: Python package +name: Python tests on: [push] diff --git a/README.md b/README.md index 871003a..19bbbe6 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Library and cli to manage and interact with your Google Drive, sheets and docs 1. [License](#license) # Introduction -![Build Status](https://github.com/eduardogr/google-drive-python/actions/workflows/python-package.yml/badge.svg?event=push) +![Build Status](https://github.com/eduardogr/google-drive-python/actions/workflows/python-tests.yml/badge.svg?event=push) [![Python](https://img.shields.io/badge/Python-v3.9%2B-blue)]() [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) [![GitHub license](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/eduardogr/google-drive-python/blob/main/LICENSE)