gyomu-schema 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gyomu_schema-0.1.1/.gitignore +19 -0
- gyomu_schema-0.1.1/PKG-INFO +12 -0
- gyomu_schema-0.1.1/README.md +3 -0
- gyomu_schema-0.1.1/pyproject.toml +17 -0
- gyomu_schema-0.1.1/src/gyomu_schema/__init__.py +0 -0
- gyomu_schema-0.1.1/src/gyomu_schema/config/config_loader_option.py +35 -0
- gyomu_schema-0.1.1/src/gyomu_schema/convert.py +38 -0
- gyomu_schema-0.1.1/src/gyomu_schema/db/config.py +5 -0
- gyomu_schema-0.1.1/src/gyomu_schema/error/__init__.py +16 -0
- gyomu_schema-0.1.1/src/gyomu_schema/error/base.py +56 -0
- gyomu_schema-0.1.1/src/gyomu_schema/error/config.py +30 -0
- gyomu_schema-0.1.1/src/gyomu_schema/error/database.py +5 -0
- gyomu_schema-0.1.1/src/gyomu_schema/error/gyomu.py +38 -0
- gyomu_schema-0.1.1/src/gyomu_schema/error/io.py +5 -0
- gyomu_schema-0.1.1/src/gyomu_schema/error/timeout.py +28 -0
- gyomu_schema-0.1.1/src/gyomu_schema/error/validation.py +24 -0
- gyomu_schema-0.1.1/src/gyomu_schema/filesystem/__init__.py +0 -0
- gyomu_schema-0.1.1/src/gyomu_schema/filesystem/file.py +213 -0
- gyomu_schema-0.1.1/src/gyomu_schema/gyomu/holiday/business_calendar.py +68 -0
- gyomu_schema-0.1.1/src/gyomu_schema/gyomu/parameter/parameter_access.py +94 -0
- gyomu_schema-0.1.1/src/gyomu_schema/gyomu/user.py +6 -0
- gyomu_schema-0.1.1/src/gyomu_schema/gyomu/variable/variable_translator.py +114 -0
- gyomu_schema-0.1.1/src/gyomu_schema/logger/config.py +21 -0
- gyomu_schema-0.1.1/src/gyomu_schema/logger/logger.py +46 -0
- gyomu_schema-0.1.1/src/gyomu_schema/market_holiday.py +10 -0
- gyomu_schema-0.1.1/src/gyomu_schema/parameter/__init__.py +0 -0
- gyomu_schema-0.1.1/src/gyomu_schema/parameter/parameter_master.py +26 -0
- gyomu_schema-0.1.1/src/gyomu_schema/py.typed +0 -0
- gyomu_schema-0.1.1/src/gyomu_schema/utility/polling.py +47 -0
- gyomu_schema-0.1.1/tests/unit/conversion/test_convert.py +121 -0
- gyomu_schema-0.1.1/tests/unit/error/test_base.py +38 -0
- gyomu_schema-0.1.1/tests/unit/error/test_errors.py +47 -0
- gyomu_schema-0.1.1/tests/unit/error/test_gyomu.py +46 -0
- gyomu_schema-0.1.1/tests/unit/filesystem/test_file.py +594 -0
- gyomu_schema-0.1.1/tests/unit/gyomu/holiday/test_market_holiday.py +15 -0
- gyomu_schema-0.1.1/tests/unit/logger/test_logger_config.py +90 -0
- gyomu_schema-0.1.1/tests/unit/utility/test_polling.py +83 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gyomu-schema
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Gyomu canonical schemas
|
|
5
|
+
Requires-Python: >=3.14
|
|
6
|
+
Requires-Dist: pydantic<3,>=2
|
|
7
|
+
Requires-Dist: returns>=0.29.0
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# gyomu-schema
|
|
11
|
+
|
|
12
|
+
Canonical schemas and common types for Gyomu Python.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gyomu-schema"
|
|
3
|
+
dynamic = ["version"]
|
|
4
|
+
description = "Gyomu canonical schemas"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.14"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"pydantic>=2,<3",
|
|
9
|
+
"returns>=0.29.0",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["hatchling"]
|
|
14
|
+
build-backend = "hatchling.build"
|
|
15
|
+
|
|
16
|
+
[tool.hatch.version]
|
|
17
|
+
path = "../../version/__about__.py"
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class EnvironmentLoaderOption:
|
|
8
|
+
variables: Mapping[str, str]
|
|
9
|
+
use_dot_env: bool = False
|
|
10
|
+
dot_env_path: Path | None = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class BaseFileLoaderOption:
|
|
15
|
+
file_path: Path
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class JsonLoaderOption(BaseFileLoaderOption):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class YamlLoaderOption(BaseFileLoaderOption):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class TomlLoaderOption(BaseFileLoaderOption):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
type ConfigLoaderOption = (
|
|
34
|
+
EnvironmentLoaderOption | JsonLoaderOption | YamlLoaderOption | TomlLoaderOption
|
|
35
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from pydantic import ValidationError as PydanticValidationError
|
|
5
|
+
from returns.result import Failure, Result, Success
|
|
6
|
+
|
|
7
|
+
from gyomu_schema.error.io import GyomuIOError
|
|
8
|
+
from gyomu_schema.error.validation import ValidationError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def convert[T: BaseModel](
|
|
12
|
+
schema: type[T],
|
|
13
|
+
value: object,
|
|
14
|
+
) -> Result[T, ValidationError]:
|
|
15
|
+
try:
|
|
16
|
+
return Success(schema.model_validate(value))
|
|
17
|
+
except PydanticValidationError as exc:
|
|
18
|
+
return Failure(
|
|
19
|
+
ValidationError(
|
|
20
|
+
message="Schema validation failed",
|
|
21
|
+
input=value,
|
|
22
|
+
details={
|
|
23
|
+
"issues": exc.errors(),
|
|
24
|
+
},
|
|
25
|
+
).chain(exc)
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def convert_json[T: BaseModel](
|
|
30
|
+
schema: type[T],
|
|
31
|
+
content: str,
|
|
32
|
+
) -> Result[T, GyomuIOError | ValidationError]:
|
|
33
|
+
try:
|
|
34
|
+
value = json.loads(content)
|
|
35
|
+
except json.JSONDecodeError as exc:
|
|
36
|
+
return Failure(GyomuIOError("Fail to load JSON").chain(exc))
|
|
37
|
+
|
|
38
|
+
return convert(schema, value)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from gyomu_schema.error.base import BaseError
|
|
2
|
+
from gyomu_schema.error.config import ConfigError
|
|
3
|
+
from gyomu_schema.error.database import DatabaseError
|
|
4
|
+
from gyomu_schema.error.gyomu import GyomuError, GyomuErrorReason
|
|
5
|
+
from gyomu_schema.error.io import GyomuIOError
|
|
6
|
+
from gyomu_schema.error.validation import ValidationError
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"BaseError",
|
|
10
|
+
"ConfigError",
|
|
11
|
+
"DatabaseError",
|
|
12
|
+
"GyomuError",
|
|
13
|
+
"GyomuErrorReason",
|
|
14
|
+
"GyomuIOError",
|
|
15
|
+
"ValidationError",
|
|
16
|
+
]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from typing import Self
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BaseError(Exception):
|
|
6
|
+
"""Base class for Gyomu application errors.
|
|
7
|
+
|
|
8
|
+
Provides common contextual information for errors in addition to the
|
|
9
|
+
information already provided by Python's :class:`Exception`.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
message: A human-readable description of the error.
|
|
13
|
+
context: An error context identifier.
|
|
14
|
+
details: Additional structured diagnostic information.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
context: The error context identifier.
|
|
18
|
+
details: Additional structured diagnostic information.
|
|
19
|
+
|
|
20
|
+
Gyomu Context:
|
|
21
|
+
context identifies the operation or location where the error occurred.
|
|
22
|
+
It describes what the application was doing when the error occurred
|
|
23
|
+
and does not replace the Python traceback.
|
|
24
|
+
|
|
25
|
+
details contains additional structured information useful for
|
|
26
|
+
diagnosing the error, such as relevant input values, configuration
|
|
27
|
+
metadata, or other operational information.
|
|
28
|
+
|
|
29
|
+
The original cause of an error is represented by Python's exception
|
|
30
|
+
chaining mechanism (__cause__). The :meth:`chain` method can be used
|
|
31
|
+
to explicitly associate an underlying exception as the cause.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
message: str,
|
|
37
|
+
*,
|
|
38
|
+
context: str | None = None,
|
|
39
|
+
details: Mapping[str, object] | None = None,
|
|
40
|
+
) -> None:
|
|
41
|
+
"""Initialize a BaseError."""
|
|
42
|
+
super().__init__(message)
|
|
43
|
+
self.context = context
|
|
44
|
+
self.details = details
|
|
45
|
+
|
|
46
|
+
def chain(self, cause: BaseException) -> Self:
|
|
47
|
+
"""Associate an exception as the explicit cause of this error.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
cause: The underlying exception that caused this error.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
This error instance.
|
|
54
|
+
"""
|
|
55
|
+
self.__cause__ = cause
|
|
56
|
+
return self
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from gyomu_schema.error.base import BaseError
|
|
5
|
+
|
|
6
|
+
type ConfigPhase = Literal["load", "parse", "decode", "validate"]
|
|
7
|
+
type ConfigSource = Literal["env", "yaml", "json", "toml"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ConfigError(BaseError):
|
|
11
|
+
"""Configuration error."""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
message: str,
|
|
16
|
+
*,
|
|
17
|
+
source: ConfigSource,
|
|
18
|
+
schema: object,
|
|
19
|
+
phase: ConfigPhase,
|
|
20
|
+
context: str | None = None,
|
|
21
|
+
details: Mapping[str, object] | None = None,
|
|
22
|
+
) -> None:
|
|
23
|
+
super().__init__(
|
|
24
|
+
message,
|
|
25
|
+
context=context,
|
|
26
|
+
details=details,
|
|
27
|
+
)
|
|
28
|
+
self.source = source
|
|
29
|
+
self.schema = schema
|
|
30
|
+
self.phase = phase
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from gyomu_schema.error.base import BaseError
|
|
5
|
+
|
|
6
|
+
GyomuErrorReason = Literal[
|
|
7
|
+
"invalid_input",
|
|
8
|
+
"not_found",
|
|
9
|
+
"external_failure",
|
|
10
|
+
"unexpected",
|
|
11
|
+
"concurrent_modification",
|
|
12
|
+
"out_of_bounds",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GyomuError(BaseError):
|
|
17
|
+
"""Application-level Gyomu error."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
message: str,
|
|
22
|
+
*,
|
|
23
|
+
operation: str,
|
|
24
|
+
domain: str,
|
|
25
|
+
reason: GyomuErrorReason,
|
|
26
|
+
retryable: bool = False,
|
|
27
|
+
context: str | None = None,
|
|
28
|
+
details: Mapping[str, object] | None = None,
|
|
29
|
+
) -> None:
|
|
30
|
+
super().__init__(
|
|
31
|
+
message,
|
|
32
|
+
context=context,
|
|
33
|
+
details=details,
|
|
34
|
+
)
|
|
35
|
+
self.operation = operation
|
|
36
|
+
self.domain = domain
|
|
37
|
+
self.reason = reason
|
|
38
|
+
self.retryable = retryable
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
|
|
3
|
+
from gyomu_schema.error.base import BaseError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class GyomuTimeoutError(BaseError):
|
|
7
|
+
"""Timeout related error."""
|
|
8
|
+
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
message: str,
|
|
12
|
+
*,
|
|
13
|
+
action: str,
|
|
14
|
+
timeout_seconds: float,
|
|
15
|
+
interval_seconds: float | None = None,
|
|
16
|
+
elapsed_seconds: float | None = None,
|
|
17
|
+
context: str | None = None,
|
|
18
|
+
details: Mapping[str, object] | None = None,
|
|
19
|
+
) -> None:
|
|
20
|
+
super().__init__(
|
|
21
|
+
message,
|
|
22
|
+
context=context,
|
|
23
|
+
details=details,
|
|
24
|
+
)
|
|
25
|
+
self.action = action
|
|
26
|
+
self.timeout_seconds = timeout_seconds
|
|
27
|
+
self.interval_seconds = interval_seconds
|
|
28
|
+
self.elapsed_seconds = elapsed_seconds
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
|
|
3
|
+
from gyomu_schema.error.base import BaseError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ValidationError(BaseError):
|
|
7
|
+
"""Validation error."""
|
|
8
|
+
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
message: str,
|
|
12
|
+
*,
|
|
13
|
+
location: tuple[str | int, ...] | None = None,
|
|
14
|
+
input: object | None = None,
|
|
15
|
+
context: str | None = None,
|
|
16
|
+
details: Mapping[str, object] | None = None,
|
|
17
|
+
) -> None:
|
|
18
|
+
super().__init__(
|
|
19
|
+
message,
|
|
20
|
+
context=context,
|
|
21
|
+
details=details,
|
|
22
|
+
)
|
|
23
|
+
self.location = location
|
|
24
|
+
self.input = input
|
|
File without changes
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
from datetime import UTC, datetime
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from os import path
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FileInfo:
|
|
8
|
+
@staticmethod
|
|
9
|
+
def epoch_to_datetimeutc(epoch: float) -> datetime:
|
|
10
|
+
return datetime.fromtimestamp(epoch, tz=UTC)
|
|
11
|
+
|
|
12
|
+
file_name: str
|
|
13
|
+
full_path: Path
|
|
14
|
+
dir_name: str
|
|
15
|
+
dir_path: Path
|
|
16
|
+
size: int
|
|
17
|
+
extension: str
|
|
18
|
+
create_time_utc: datetime
|
|
19
|
+
update_time_utc: datetime
|
|
20
|
+
last_access_time_utc: datetime
|
|
21
|
+
|
|
22
|
+
def __init__(self, file_path: Path):
|
|
23
|
+
p = file_path
|
|
24
|
+
self.file_name = p.name
|
|
25
|
+
self.full_path = p.resolve()
|
|
26
|
+
self.dir_name = p.parent.name
|
|
27
|
+
self.dir_path = p.parent
|
|
28
|
+
self.size = path.getsize(file_path)
|
|
29
|
+
self.extension = "".join(p.suffix)
|
|
30
|
+
self.create_time_utc = FileInfo.epoch_to_datetimeutc(path.getctime(p))
|
|
31
|
+
self.update_time_utc = FileInfo.epoch_to_datetimeutc(path.getmtime(p))
|
|
32
|
+
self.last_access_time_utc = FileInfo.epoch_to_datetimeutc(path.getatime(p))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class FileFilterType(Enum):
|
|
36
|
+
FILE_NAME = "file_name"
|
|
37
|
+
CREATE_TIME_UTC = "create_time_utc"
|
|
38
|
+
LAST_ACCESS_TIME_UTC = "last_access_time_utc"
|
|
39
|
+
LAST_WRITE_TIME_UTC = "last_write_time_utc"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class FileCompareType(Enum):
|
|
43
|
+
EQUAL = "equal"
|
|
44
|
+
LARGER = "larger"
|
|
45
|
+
LESS = "less"
|
|
46
|
+
LARGER_OR_EQUAL = "larger_or_equal"
|
|
47
|
+
LESS_OR_EQUAL = "less_or_equal"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class FileFilterInfo:
|
|
51
|
+
name_filter: str
|
|
52
|
+
target_date: datetime
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
kind: FileFilterType,
|
|
57
|
+
operator: FileCompareType,
|
|
58
|
+
value: object,
|
|
59
|
+
):
|
|
60
|
+
self.kind = kind
|
|
61
|
+
self.operator = operator
|
|
62
|
+
|
|
63
|
+
if self.kind == FileFilterType.FILE_NAME:
|
|
64
|
+
self.name_filter = str(value)
|
|
65
|
+
elif isinstance(value, datetime):
|
|
66
|
+
self.target_date = value
|
|
67
|
+
elif isinstance(value, str):
|
|
68
|
+
try:
|
|
69
|
+
self.target_date = datetime.strptime(value, "%Y%m%d")
|
|
70
|
+
except ValueError as e:
|
|
71
|
+
raise ValueError(f"Date Parameter is invalid: {value}") from e
|
|
72
|
+
else:
|
|
73
|
+
raise ValueError(f"Date Parameter is invalid: {value}")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class FileTransportInfo:
|
|
77
|
+
"""
|
|
78
|
+
Base Sdir Sname Ddir Dname (S)full+base (S)Full (S)path (S)name (D)full (D)path (D)name
|
|
79
|
+
x x x x x base/SDir/Sname SDir/Sname SDir Sname Ddir/Dname Ddir Dname
|
|
80
|
+
x x x x base/SDir/Sname SDir/Sname SDir Sname Ddir/Sname Ddir Sname
|
|
81
|
+
x x x x base/SDir/Sname SDir/Sname SDir Sname SDir/Dname Sdir Dname
|
|
82
|
+
x x x base/SDir/Sname SDir/Sname SDir Sname SDir/Sname SDir Sname
|
|
83
|
+
x x x base/SDir SDir SDir Ddir Ddir
|
|
84
|
+
x x base/SDir SDir SDir SDir SDir
|
|
85
|
+
x base
|
|
86
|
+
x x base Ddir Ddir
|
|
87
|
+
x x x x base/Sname Sname Sname Ddir/Dname Ddir Dname
|
|
88
|
+
x x x base/Sname Sname Sname Ddir/Sname Ddir Sname
|
|
89
|
+
x x x base/Sname Sname Sname Dname Dname
|
|
90
|
+
x x base/Sname Sname Sname Sname Sname
|
|
91
|
+
x x x x SDir/Sname SDir Sname Ddir/Dname Ddir Dname
|
|
92
|
+
x x x SDir/Sname SDir Sname Ddir/Sname Ddir Sname
|
|
93
|
+
x x x SDir/Sname SDir Sname SDir/Dname SDir Dname
|
|
94
|
+
x x SDir/Sname SDir Sname SDir/Sname SDir Sname
|
|
95
|
+
x x SDir SDir Ddir Ddir
|
|
96
|
+
x SDir SDir SDir SDir
|
|
97
|
+
x x x Sname Sname Ddir/Dname Ddir Dname
|
|
98
|
+
x x Sname Sname Ddir/Sname Ddir Sname
|
|
99
|
+
x x Sname Sname Dname Dname
|
|
100
|
+
x Sname Sname Sname Sname
|
|
101
|
+
""" # noqa: E501
|
|
102
|
+
|
|
103
|
+
__source_filename: str
|
|
104
|
+
__source_folder_name: str
|
|
105
|
+
__base_path: str
|
|
106
|
+
__destination_filename: str
|
|
107
|
+
__destination_folder_name: str
|
|
108
|
+
delete_sourcefile_after_completion: bool = False
|
|
109
|
+
overwrite_destination: bool = False
|
|
110
|
+
filter_conditions: list[FileFilterInfo] | None = None
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def is_source_directory(self) -> bool:
|
|
114
|
+
return not self.source_filename
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def is_destination_directory(self) -> bool:
|
|
118
|
+
return not self.destination_filename
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def is_destination_root(self) -> bool:
|
|
122
|
+
return bool(
|
|
123
|
+
not self.__source_folder_name and not self.__destination_folder_name
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def source_fullname(self) -> str:
|
|
128
|
+
if not self.__source_folder_name:
|
|
129
|
+
return self.__source_filename
|
|
130
|
+
if not self.__source_filename:
|
|
131
|
+
return self.__source_folder_name
|
|
132
|
+
return path.join(self.__source_folder_name, self.__source_filename)
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def source_fullname_with_basepath(self) -> str:
|
|
136
|
+
if not self.source_fullname:
|
|
137
|
+
return self.__base_path
|
|
138
|
+
return (
|
|
139
|
+
self.source_fullname
|
|
140
|
+
if not self.__base_path
|
|
141
|
+
else path.join(self.__base_path, self.source_fullname)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def source_path(self) -> str:
|
|
146
|
+
return self.__source_folder_name
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def source_filename(self) -> str:
|
|
150
|
+
return self.__source_filename
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def destination_filename(self) -> str:
|
|
154
|
+
return (
|
|
155
|
+
self.__source_filename
|
|
156
|
+
if not self.__destination_filename
|
|
157
|
+
else self.__destination_filename
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def destination_path(self) -> str:
|
|
162
|
+
return (
|
|
163
|
+
self.__source_folder_name
|
|
164
|
+
if not self.__destination_folder_name
|
|
165
|
+
else self.__destination_folder_name
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def destination_fullname(self) -> str:
|
|
170
|
+
if not self.destination_path:
|
|
171
|
+
return self.destination_filename
|
|
172
|
+
if not self.destination_filename:
|
|
173
|
+
return self.destination_path
|
|
174
|
+
return path.join(self.destination_path, self.destination_filename)
|
|
175
|
+
|
|
176
|
+
def __init__(
|
|
177
|
+
self,
|
|
178
|
+
base_path: str = "",
|
|
179
|
+
source_filename: str = "",
|
|
180
|
+
source_folder_name: str = "",
|
|
181
|
+
destination_filename: str = "",
|
|
182
|
+
destination_foldername: str = "",
|
|
183
|
+
delete_sourcefile_after_completion: bool = False,
|
|
184
|
+
overwrite_destination: bool = False,
|
|
185
|
+
filter_conditions: list[FileFilterInfo] | None = None,
|
|
186
|
+
):
|
|
187
|
+
self.__base_path = base_path
|
|
188
|
+
self.__source_filename = source_filename
|
|
189
|
+
self.__source_folder_name = source_folder_name
|
|
190
|
+
self.__destination_filename = destination_filename
|
|
191
|
+
self.__destination_folder_name = destination_foldername
|
|
192
|
+
self.delete_sourcefile_after_completion = delete_sourcefile_after_completion
|
|
193
|
+
self.overwrite_destination = overwrite_destination
|
|
194
|
+
self.filter_conditions = filter_conditions
|
|
195
|
+
|
|
196
|
+
if not self.__source_filename and self.__destination_filename:
|
|
197
|
+
raise ValueError("Invalid Parameter")
|
|
198
|
+
|
|
199
|
+
if (
|
|
200
|
+
not self.__base_path
|
|
201
|
+
and not self.__source_folder_name
|
|
202
|
+
and not self.__source_filename
|
|
203
|
+
):
|
|
204
|
+
raise ValueError("Invalid Parameter")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class FileArchiveType(Enum):
|
|
208
|
+
ZIP = ("zip",)
|
|
209
|
+
TGZ = ("tgz",)
|
|
210
|
+
BZIP2 = ("bz2",)
|
|
211
|
+
GZIP = ("gz",)
|
|
212
|
+
TAR = ("tar",)
|
|
213
|
+
GuessFromFileName = "unknown"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from typing import Protocol
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BusinessCalendar(Protocol):
|
|
6
|
+
def is_business_day(
|
|
7
|
+
self,
|
|
8
|
+
target_date: date,
|
|
9
|
+
) -> bool: ...
|
|
10
|
+
|
|
11
|
+
def business_day(
|
|
12
|
+
self,
|
|
13
|
+
target_date: date,
|
|
14
|
+
day_offset: int,
|
|
15
|
+
) -> date: ...
|
|
16
|
+
|
|
17
|
+
def get_holidays(
|
|
18
|
+
self,
|
|
19
|
+
start_date: date,
|
|
20
|
+
end_date: date,
|
|
21
|
+
) -> list[date]: ...
|
|
22
|
+
|
|
23
|
+
def business_day_of_beginning_month_with_offset(
|
|
24
|
+
self,
|
|
25
|
+
target_date: date,
|
|
26
|
+
day_offset: int = 1,
|
|
27
|
+
) -> date: ...
|
|
28
|
+
|
|
29
|
+
def business_day_of_beginning_of_next_month_with_offset(
|
|
30
|
+
self,
|
|
31
|
+
target_date: date,
|
|
32
|
+
day_offset: int = 1,
|
|
33
|
+
) -> date: ...
|
|
34
|
+
|
|
35
|
+
def business_day_of_beginning_of_previous_month_with_offset(
|
|
36
|
+
self,
|
|
37
|
+
target_date: date,
|
|
38
|
+
day_offset: int = 1,
|
|
39
|
+
) -> date: ...
|
|
40
|
+
def business_day_of_end_month_with_offset(
|
|
41
|
+
self,
|
|
42
|
+
target_date: date,
|
|
43
|
+
day_offset: int,
|
|
44
|
+
) -> date: ...
|
|
45
|
+
|
|
46
|
+
def business_day_of_end_of_next_month_with_offset(
|
|
47
|
+
self,
|
|
48
|
+
target_date: date,
|
|
49
|
+
day_offset: int,
|
|
50
|
+
) -> date: ...
|
|
51
|
+
|
|
52
|
+
def business_day_of_end_of_previous_month_with_offset(
|
|
53
|
+
self,
|
|
54
|
+
target_date: date,
|
|
55
|
+
day_offset: int,
|
|
56
|
+
) -> date: ...
|
|
57
|
+
|
|
58
|
+
def business_day_of_beginning_of_year(
|
|
59
|
+
self,
|
|
60
|
+
target_date: date,
|
|
61
|
+
day_offset: int,
|
|
62
|
+
) -> date: ...
|
|
63
|
+
|
|
64
|
+
def business_day_of_end_of_year(
|
|
65
|
+
self,
|
|
66
|
+
target_date: date,
|
|
67
|
+
day_offset: int,
|
|
68
|
+
) -> date: ...
|