Skip to content

Commit

Permalink
feature/enable-python-3.8-and-fix-mypy (#32)
Browse files Browse the repository at this point in the history
* feature/enable-python-3.8-and-fix-mypy

* remove python 3.7 from publish

* change typings for py 3.8
  • Loading branch information
IamTugy authored Aug 10, 2024
1 parent 6cc7cda commit 370c67c
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

services:
minio:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:

strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
Expand Down
26 changes: 2 additions & 24 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 25 additions & 18 deletions pyminio/main.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
from collections import deque
from datetime import datetime
from datetime import datetime, timezone
from functools import wraps
from io import BytesIO
from os.path import basename, join, normpath
from posixpath import dirname
from typing import Iterable
from typing import Any, Dict, Iterable, List, Tuple, Union

import pytz
from minio import Minio, datatypes
from minio.commonconfig import CopySource
from minio.error import S3Error
from urllib3 import HTTPHeaderDict

from .exceptions import DirectoryNotEmptyError
from .structures import ROOT, File, Folder, Match, ObjectData

METADATA_TYPE = Union[Dict[str, Union[str, List[str], Tuple[str]]], None]


def _validate_directory(func):
"""Check if directory path is valid."""
Expand All @@ -35,15 +37,15 @@ def decorated_method(self, path: str, *args, **kwargs):
def get_last_modified(obj):
"""Return object's last modified time."""
if obj.last_modified is None:
return pytz.UTC.localize(datetime.fromtimestamp(0))
return datetime.fromtimestamp(0, tz=timezone.utc)

return obj.last_modified


def get_creation_date(obj):
"""Return object's creation date."""
if obj.creation_date is None:
return pytz.UTC.localize(datetime.fromtimestamp(0))
return datetime.fromtimestamp(0, tz=timezone.utc)
return obj.creation_date


Expand Down Expand Up @@ -100,15 +102,15 @@ def mkdirs(self, path: str):
@staticmethod
def _remove_current_from_object_list(
objects: Iterable[datatypes.Object], match: Match
) -> list[datatypes.Object]:
) -> List[datatypes.Object]:
"""When finding results list_objects return the given path aswell."""
return [
obj
for obj in objects
if match.path != f"/{obj.bucket_name}/{obj.object_name}"
]

def _get_objects_at(self, match: Match) -> list[datatypes.Object]:
def _get_objects_at(self, match: Match) -> List[datatypes.Object]:
"""Return all objects in the specified bucket and directory path.
Args:
Expand All @@ -133,7 +135,9 @@ def _get_buckets(self):
)

@classmethod
def _extract_metadata(cls, detailed_metadata: dict) -> dict[str, str]:
def _extract_metadata(
cls, detailed_metadata: Union[HTTPHeaderDict, Dict[str, str], None]
) -> Dict[str, Any]:
"""Remove 'X-Amz-Meta-' from all the keys, and lowercase them.
When metadata is pushed in the minio, the minio is adding
those details that screw us. this is an unscrewing function.
Expand All @@ -147,7 +151,7 @@ def _extract_metadata(cls, detailed_metadata: dict) -> dict[str, str]:
@_validate_directory
def listdir(
self, path: str, files_only: bool = False, dirs_only: bool = False
) -> tuple[str]:
) -> tuple:
"""Return all files and directories within the directory path.
Works like os.listdir.
Expand All @@ -169,7 +173,7 @@ def listdir(
return tuple(f"{b.name}/" for b in self._get_buckets())

return tuple(
obj.object_name.replace(match.prefix, "")
(obj.object_name or "").replace(match.prefix, "")
for obj in self._get_objects_at(match)
if not (files_only and obj.is_dir or dirs_only and not obj.is_dir)
)
Expand Down Expand Up @@ -215,16 +219,16 @@ def truncate(self) -> "Pyminio":
self.rmdir(join(ROOT, bucket), recursive=True)
return self

def _remove_root(self, recursive: bool) -> None:
def _remove_root(self, recursive: bool) -> "Pyminio":
if recursive:
return self.truncate()
raise DirectoryNotEmptyError(
"can not recursively delete non-empty root directory, use the recursive flag."
)

def _remove_content(self, objects: list[datatypes.Object]) -> None:
def _remove_content(self, objects: List[datatypes.Object]) -> None:
for obj in objects:
self.minio_obj.remove_object(obj.bucket_name, obj.object_name)
self.minio_obj.remove_object(obj.bucket_name, obj.object_name or "")

def _remove_bucket(self, bucket: str) -> None:
try:
Expand Down Expand Up @@ -354,7 +358,7 @@ def copy_recursively(self, from_path: str, to_path: str) -> "Pyminio":
dirs_to_copy.extend(dirs)

for obj_to_copy in files_to_copy:
self.cp(**obj_to_copy)
self.cp(from_path=obj_to_copy["from_path"], to_path=obj_to_copy["to_path"])

return self

Expand Down Expand Up @@ -418,6 +422,7 @@ def get(self, path: str) -> ObjectData:
path: path of a directory or a file.
"""
match = Match(path)
return_obj: type[ObjectData]
kwargs = {}

if match.is_bucket():
Expand All @@ -441,7 +446,7 @@ def get(self, path: str) -> ObjectData:
details = next(
obj for obj in objects if obj.object_name == match.relative_path
)
name = join(basename(normpath(details.object_name)), "")
name = join(basename(normpath(details.object_name or "")), "")
return_obj = Folder

except StopIteration:
Expand All @@ -463,7 +468,7 @@ def get(self, path: str) -> ObjectData:

return return_obj(name=name, full_path=path, metadata=metadata, **kwargs)

def put_data(self, path: str, data: bytes, metadata: dict = None):
def put_data(self, path: str, data: bytes, metadata: METADATA_TYPE = None):
"""Put data in file inside a minio folder.
Args:
Expand All @@ -483,7 +488,9 @@ def put_data(self, path: str, data: bytes, metadata: dict = None):
)
data_file.close()

def put_file(self, file_path: str, to_path: str, metadata: dict = None):
def put_file(
self, file_path: str, to_path: str, metadata: METADATA_TYPE = None
) -> None:
"""Put file inside a minio folder.
If file_path will be a path to a file, the name will be
Expand All @@ -505,7 +512,7 @@ def put_file(self, file_path: str, to_path: str, metadata: dict = None):
)

@_validate_directory
def get_last_object(self, path: str) -> File:
def get_last_object(self, path: str) -> Union[ObjectData, None]:
"""Return the last modified object.
Args:
Expand Down
12 changes: 7 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
[tool.poetry]
name = "pyminio"
version = "0.3.0"
version = "0.3.1"
description = "Python client for Minio"
authors = ["Michael Tugendhaft <[email protected]>"]
license = "MIT"
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.9"
python = "^3.8.1"
minio = ">=7.2.7"
pytz = "^2024.1"


[tool.poetry.group.dev.dependencies]
isort = "^5.13.2"
black = "^24.8.0"
flake8 = "^7.1.1"
flake8 = "^7.1.0"
pytest = "^8.3.2"
mypy = "^1.11.1"
types-pytz = "^2024.1.0.20240417"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.isort]
profile = "black"
atomic = true

0 comments on commit 370c67c

Please sign in to comment.