diff --git a/files/README.md b/files/README.md index 024a20bc9..a482ea390 100755 --- a/files/README.md +++ b/files/README.md @@ -14,23 +14,16 @@ pip install codat-files import codat from codat.models import operations, shared -s = codat.Codat() -s.config_security( +s = codat.Codat( security=shared.Security( - api_key="YOUR_API_KEY_HERE", - ) + auth_header="YOUR_API_KEY_HERE", + ), ) - + + req = operations.DownloadFilesRequest( - security=operations.DownloadFilesSecurity( - api_key="YOUR_API_KEY_HERE", - ), - path_params=operations.DownloadFilesPathParams( - company_id="unde", - ), - query_params=operations.DownloadFilesQueryParams( - date_="2022-07-28T14:41:43.209Z", - ), + company_id="unde", + date_="2022-08-10T14:41:25.900Z", ) res = s.files.download_files(req) diff --git a/files/RELEASES.md b/files/RELEASES.md index b0f55d48f..4fbd11112 100644 --- a/files/RELEASES.md +++ b/files/RELEASES.md @@ -14,4 +14,68 @@ Based on: - OpenAPI Doc 2.1.0 https://raw.githubusercontent.com/codatio/oas/main/yaml/Codat-Files.yaml - Speakeasy CLI 1.7.1 https://github.com/speakeasy-api/speakeasy ### Releases -- [PyPI v0.1.1] https://pypi.org/project/codat-files/0.1.1 - files \ No newline at end of file +- [PyPI v0.1.1] https://pypi.org/project/codat-files/0.1.1 - files + +## 2023-03-04 00:00:26 +### Changes +Based on: +- OpenAPI Doc 2.1.0 https://raw.githubusercontent.com/codatio/oas/main/yaml/Codat-Files.yaml +- Speakeasy CLI 1.8.2 https://github.com/speakeasy-api/speakeasy +### Releases +- [PyPI v0.2.0] https://pypi.org/project/codat-files/0.2.0 - files + +## 2023-03-06 00:00:33 +### Changes +Based on: +- OpenAPI Doc 2.1.0 https://raw.githubusercontent.com/codatio/oas/main/yaml/Codat-Files.yaml +- Speakeasy CLI 1.8.4 https://github.com/speakeasy-api/speakeasy +### Releases +- [PyPI v0.2.1] https://pypi.org/project/codat-files/0.2.1 - files + +## 2023-03-07 00:00:54 +### Changes +Based on: +- OpenAPI Doc 2.1.0 https://raw.githubusercontent.com/codatio/oas/main/yaml/Codat-Files.yaml +- Speakeasy CLI 1.8.5 https://github.com/speakeasy-api/speakeasy +### Releases +- [PyPI v0.2.2] https://pypi.org/project/codat-files/0.2.2 - files + +## 2023-03-08 00:00:52 +### Changes +Based on: +- OpenAPI Doc 2.1.0 https://raw.githubusercontent.com/codatio/oas/main/yaml/Codat-Files.yaml +- Speakeasy CLI 1.8.6 https://github.com/speakeasy-api/speakeasy +### Releases +- [PyPI v0.2.3] https://pypi.org/project/codat-files/0.2.3 - files + +## 2023-03-09 00:00:49 +### Changes +Based on: +- OpenAPI Doc 2.1.0 https://raw.githubusercontent.com/codatio/oas/main/yaml/Codat-Files.yaml +- Speakeasy CLI 1.8.7 https://github.com/speakeasy-api/speakeasy +### Releases +- [PyPI v0.2.4] https://pypi.org/project/codat-files/0.2.4 - files + +## 2023-03-10 00:00:48 +### Changes +Based on: +- OpenAPI Doc 2.1.0 https://raw.githubusercontent.com/codatio/oas/main/yaml/Codat-Files.yaml +- Speakeasy CLI 1.9.1 https://github.com/speakeasy-api/speakeasy +### Releases +- [PyPI v0.3.0] https://pypi.org/project/codat-files/0.3.0 - files + +## 2023-03-11 00:00:44 +### Changes +Based on: +- OpenAPI Doc 2.1.0 https://raw.githubusercontent.com/codatio/oas/main/yaml/Codat-Files.yaml +- Speakeasy CLI 1.9.2 https://github.com/speakeasy-api/speakeasy +### Releases +- [PyPI v0.3.1] https://pypi.org/project/codat-files/0.3.1 - files + +## 2023-03-15 00:00:27 +### Changes +Based on: +- OpenAPI Doc 2.1.0 https://raw.githubusercontent.com/codatio/oas/main/yaml/Codat-Files.yaml +- Speakeasy CLI 1.11.0 https://github.com/speakeasy-api/speakeasy +### Releases +- [PyPI v0.4.0] https://pypi.org/project/codat-files/0.4.0 - files \ No newline at end of file diff --git a/files/USAGE.md b/files/USAGE.md index 8fa765e58..fd8567674 100755 --- a/files/USAGE.md +++ b/files/USAGE.md @@ -3,23 +3,16 @@ import codat from codat.models import operations, shared -s = codat.Codat() -s.config_security( +s = codat.Codat( security=shared.Security( - api_key="YOUR_API_KEY_HERE", - ) + auth_header="YOUR_API_KEY_HERE", + ), ) - + + req = operations.DownloadFilesRequest( - security=operations.DownloadFilesSecurity( - api_key="YOUR_API_KEY_HERE", - ), - path_params=operations.DownloadFilesPathParams( - company_id="unde", - ), - query_params=operations.DownloadFilesQueryParams( - date_="2022-07-28T14:41:43.209Z", - ), + company_id="unde", + date_="2022-08-10T14:41:25.900Z", ) res = s.files.download_files(req) diff --git a/files/files.gen b/files/files.gen index 67a282e58..e9798c631 100755 --- a/files/files.gen +++ b/files/files.gen @@ -1,5 +1,6 @@ src/codat/files.py src/codat/sdk.py +pylintrc setup.py src/codat/__init__.py src/codat/models/__init__.py diff --git a/files/gen.yaml b/files/gen.yaml index 92158519e..890ee6d9e 100644 --- a/files/gen.yaml +++ b/files/gen.yaml @@ -1,14 +1,14 @@ configVersion: 1.0.0 management: - docChecksum: cca5233035a11c68cfe55b187722a245 + docChecksum: 4bb6506e47b59a3c0e9b865f5442867a docVersion: 2.1.0 - speakeasyVersion: 1.7.1 + speakeasyVersion: 1.11.0 generation: telemetryEnabled: false sdkClassName: codat sdkFlattening: true python: - version: 0.1.1 + version: 0.4.0 author: Speakeasy description: Python Client SDK Generated by Speakeasy packageName: codat-files diff --git a/files/pylintrc b/files/pylintrc new file mode 100755 index 000000000..4bfd3da08 --- /dev/null +++ b/files/pylintrc @@ -0,0 +1,640 @@ +[MAIN] + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Clear in-memory caches upon conclusion of linting. Useful if running pylint +# in a server-like mode. +clear-cache-post-run=no + +# Load and enable all available extensions. Use --list-extensions to see a list +# all available extensions. +#enable-all-extensions= + +# In error mode, messages with a category besides ERROR or FATAL are +# suppressed, and no reports are done by default. Error mode is compatible with +# disabling specific errors. +#errors-only= + +# Always return a 0 (non-error) status code, even if lint errors are found. +# This is primarily useful in continuous integration scripts. +#exit-zero= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-allow-list= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) +extension-pkg-whitelist= + +# Return non-zero exit code if any of these messages/categories are detected, +# even if score is above --fail-under value. Syntax same as enable. Messages +# specified are enabled, while categories only check already-enabled messages. +fail-on= + +# Specify a score threshold under which the program will exit with error. +fail-under=10 + +# Interpret the stdin as a python script, whose filename needs to be passed as +# the module_or_package argument. +#from-stdin= + +# Files or directories to be skipped. They should be base names, not paths. +ignore=CVS + +# Add files or directories matching the regular expressions patterns to the +# ignore-list. The regex matches against paths and can be in Posix or Windows +# format. Because '\\' represents the directory delimiter on Windows systems, +# it can't be used as an escape character. +ignore-paths= + +# Files or directories matching the regular expression patterns are skipped. +# The regex matches against base names, not paths. The default value ignores +# Emacs file locks +ignore-patterns=^\.# + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Minimum Python version to use for version dependent checks. Will default to +# the version used to run pylint. +py-version=3.9 + +# Discover python modules and packages in the file system subtree. +recursive=no + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# In verbose mode, extra non-checker-related info will be displayed. +#verbose= + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. If left empty, argument names will be checked with the set +# naming style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. If left empty, attribute names will be checked with the set naming +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. If left empty, class attribute names will be checked +# with the set naming style. +#class-attribute-rgx= + +# Naming style matching correct class constant names. +class-const-naming-style=UPPER_CASE + +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. If left empty, class constant names will be checked with +# the set naming style. +#class-const-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. If left empty, class names will be checked with the set naming style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. If left empty, constant names will be checked with the set naming +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. If left empty, function names will be checked with the set +# naming style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. If left empty, inline iteration names will be checked +# with the set naming style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. If left empty, method names will be checked with the set naming style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. If left empty, module names will be checked with the set naming style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Regular expression matching correct type variable names. If left empty, type +# variable names will be checked with the set naming style. +#typevar-rgx= + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. If left empty, variable names will be checked with the set +# naming style. +#variable-rgx= + + +[CLASSES] + +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# List of regular expressions of class ancestor names to ignore when counting +# public methods (see R0903) +exclude-too-few-public-methods= + +# List of qualified class names to ignore when counting class parents (see +# R0901) +ignored-parents= + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=25 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when caught. +overgeneral-exceptions=builtins.BaseException,builtins.Exception + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow explicit reexports by alias from a package __init__. +allow-reexport-from-package=no + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules= + +# Output a graph (.gv or any supported image format) of external dependencies +# to the given file (report RP0402 must not be disabled). +ext-import-graph= + +# Output a graph (.gv or any supported image format) of all (i.e. internal and +# external) dependencies to the given file (report RP0402 must not be +# disabled). +import-graph= + +# Output a graph (.gv or any supported image format) of internal dependencies +# to the given file (report RP0402 must not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, +# UNDEFINED. +confidence=HIGH, + CONTROL_FLOW, + INFERENCE, + INFERENCE_FAILURE, + UNDEFINED + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then re-enable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + trailing-whitespace, + line-too-long, + missing-class-docstring, + missing-module-docstring, + missing-function-docstring, + too-many-instance-attributes, + wrong-import-order, + too-many-arguments, + broad-exception-raised, + too-few-public-methods, + too-many-branches, + chained-comparison, + duplicate-code, + trailing-newlines, + too-many-public-methods, + too-many-locals + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=c-extension-no-member + + +[METHOD_ARGS] + +# List of qualified names (i.e., library.method) which require a timeout +# parameter e.g. 'requests.api.get,requests.api.post' +timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + +# Regular expression of note tags to take in consideration. +notes-rgx= + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit,argparse.parse_error + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'fatal', 'error', 'warning', 'refactor', +# 'convention', and 'info' which contain the number of messages in each +# category, as well as 'statement' which is the total number of statements +# analyzed. This score is used by the global evaluation report (RP0004). +evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +#output-format= + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[SIMILARITIES] + +# Comments are removed from the similarity computation +ignore-comments=yes + +# Docstrings are removed from the similarity computation +ignore-docstrings=yes + +# Imports are removed from the similarity computation +ignore-imports=yes + +# Signatures are removed from the similarity computation +ignore-signatures=yes + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it work, +# install the 'python-enchant' package. +spelling-dict= + +# List of comma separated words that should be considered directives if they +# appear at the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of symbolic message names to ignore for Mixin members. +ignored-checks-for-mixins=no-member, + not-async-context-manager, + not-context-manager, + attribute-defined-outside-init + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# Regex pattern to define which classes are considered mixins. +mixin-class-rgx=.*[Mm]ixin + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of names allowed to shadow builtins +allowed-redefined-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io diff --git a/files/setup.py b/files/setup.py index 8eca9766d..d4e87f3b5 100755 --- a/files/setup.py +++ b/files/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name="codat-files", - version="0.1.1", + version="0.4.0", author="Speakeasy", description="Python Client SDK Generated by Speakeasy", long_description=long_description, @@ -30,6 +30,7 @@ "typing-inspect==0.8.0", "typing_extensions==4.3.0", "urllib3==1.26.12", + "pylint==2.16.2", ], package_dir={'': 'src'}, python_requires='>=3.9' diff --git a/files/src/codat/__init__.py b/files/src/codat/__init__.py index 8d74a7d2f..0f9f50e91 100755 --- a/files/src/codat/__init__.py +++ b/files/src/codat/__init__.py @@ -1 +1 @@ -from .sdk import * \ No newline at end of file +from .sdk import * diff --git a/files/src/codat/files.py b/files/src/codat/files.py index 79b68f76c..52dc1834a 100755 --- a/files/src/codat/files.py +++ b/files/src/codat/files.py @@ -1,25 +1,24 @@ -import requests +import requests as requests_http from . import utils from codat.models import operations from typing import Optional class Files: - _client: requests.Session - _security_client: requests.Session + _client: requests_http.Session + _security_client: requests_http.Session _server_url: str _language: str _sdk_version: str _gen_version: str - def __init__(self, client: requests.Session, security_client: requests.Session, server_url: str, language: str, sdk_version: str, gen_version: str) -> None: + def __init__(self, client: requests_http.Session, security_client: requests_http.Session, server_url: str, language: str, sdk_version: str, gen_version: str) -> None: self._client = client self._security_client = security_client self._server_url = server_url self._language = language self._sdk_version = sdk_version self._gen_version = gen_version - - + def download_files(self, request: operations.DownloadFilesRequest) -> operations.DownloadFilesResponse: r"""Download all files for a company You can specify a date to download specific files for. @@ -27,23 +26,22 @@ def download_files(self, request: operations.DownloadFilesRequest) -> operations base_url = self._server_url - url = utils.generate_url(base_url, "/companies/{companyId}/files/download", request.path_params) + url = utils.generate_url(operations.DownloadFilesRequest, base_url, '/companies/{companyId}/files/download', request) - query_params = utils.get_query_params(request.query_params) + query_params = utils.get_query_params(operations.DownloadFilesRequest, request) - client = utils.configure_security_client(self._client, request.security) + client = self._security_client - r = client.request("GET", url, params=query_params) - content_type = r.headers.get("Content-Type") + http_res = client.request('GET', url, params=query_params) + content_type = http_res.headers.get('Content-Type') - res = operations.DownloadFilesResponse(status_code=r.status_code, content_type=content_type) + res = operations.DownloadFilesResponse(status_code=http_res.status_code, content_type=content_type, raw_response=http_res) - if r.status_code == 200: + if http_res.status_code == 200: pass return res - def list_files(self, request: operations.ListFilesRequest) -> operations.ListFilesResponse: r"""List all files uploaded by a company Returns an array of files that have been uploaded for a given company. @@ -51,24 +49,23 @@ def list_files(self, request: operations.ListFilesRequest) -> operations.ListFil base_url = self._server_url - url = utils.generate_url(base_url, "/companies/{companyId}/files", request.path_params) + url = utils.generate_url(operations.ListFilesRequest, base_url, '/companies/{companyId}/files', request) - client = utils.configure_security_client(self._client, request.security) + client = self._security_client - r = client.request("GET", url) - content_type = r.headers.get("Content-Type") + http_res = client.request('GET', url) + content_type = http_res.headers.get('Content-Type') - res = operations.ListFilesResponse(status_code=r.status_code, content_type=content_type) + res = operations.ListFilesResponse(status_code=http_res.status_code, content_type=content_type, raw_response=http_res) - if r.status_code == 200: - if utils.match_content_type(content_type, "application/json"): - out = utils.unmarshal_json(r.text, Optional[list[operations.ListFilesFile]]) + if http_res.status_code == 200: + if utils.match_content_type(content_type, 'application/json'): + out = utils.unmarshal_json(http_res.text, Optional[list[operations.ListFilesFile]]) res.files = out return res - def upload_files(self, request: operations.UploadFilesRequest) -> operations.UploadFilesResponse: r"""Upload files for a company Upload files @@ -76,17 +73,17 @@ def upload_files(self, request: operations.UploadFilesRequest) -> operations.Upl base_url = self._server_url - url = utils.generate_url(base_url, "/companies/{companyId}/connections/{connectionId}/files", request.path_params) + url = utils.generate_url(operations.UploadFilesRequest, base_url, '/companies/{companyId}/connections/{connectionId}/files', request) - client = utils.configure_security_client(self._client, request.security) + client = self._security_client - r = client.request("POST", url) - content_type = r.headers.get("Content-Type") + http_res = client.request('POST', url) + content_type = http_res.headers.get('Content-Type') - res = operations.UploadFilesResponse(status_code=r.status_code, content_type=content_type) + res = operations.UploadFilesResponse(status_code=http_res.status_code, content_type=content_type, raw_response=http_res) - if r.status_code == 200: + if http_res.status_code == 200: pass return res diff --git a/files/src/codat/models/operations/__init__.py b/files/src/codat/models/operations/__init__.py index 6543d5c67..a54786304 100755 --- a/files/src/codat/models/operations/__init__.py +++ b/files/src/codat/models/operations/__init__.py @@ -2,4 +2,4 @@ from .list_files import * from .upload_files import * -__all__ = ["DownloadFilesPathParams","DownloadFilesQueryParams","DownloadFilesRequest","DownloadFilesResponse","DownloadFilesSecurity","ListFilesFile","ListFilesPathParams","ListFilesRequest","ListFilesResponse","ListFilesSecurity","UploadFilesPathParams","UploadFilesRequest","UploadFilesResponse","UploadFilesSecurity"] \ No newline at end of file +__all__ = ["DownloadFilesRequest","DownloadFilesResponse","ListFilesFile","ListFilesRequest","ListFilesResponse","UploadFilesRequest","UploadFilesResponse"] diff --git a/files/src/codat/models/operations/download_files.py b/files/src/codat/models/operations/download_files.py index c9a338959..447491b3b 100755 --- a/files/src/codat/models/operations/download_files.py +++ b/files/src/codat/models/operations/download_files.py @@ -1,35 +1,19 @@ from __future__ import annotations import dataclasses -import dateutil.parser +import requests as requests_http from datetime import datetime -from marshmallow import fields from typing import Optional @dataclasses.dataclass -class DownloadFilesPathParams: +class DownloadFilesRequest: company_id: str = dataclasses.field(metadata={'path_param': { 'field_name': 'companyId', 'style': 'simple', 'explode': False }}) - - -@dataclasses.dataclass -class DownloadFilesQueryParams: date_: Optional[datetime] = dataclasses.field(default=None, metadata={'query_param': { 'field_name': 'date', 'style': 'form', 'explode': True }}) -@dataclasses.dataclass -class DownloadFilesSecurity: - api_key: str = dataclasses.field(metadata={'security': { 'scheme': True, 'type': 'apiKey', 'sub_type': 'header', 'field_name': 'Authorization' }}) - - -@dataclasses.dataclass -class DownloadFilesRequest: - path_params: DownloadFilesPathParams = dataclasses.field() - query_params: DownloadFilesQueryParams = dataclasses.field() - security: DownloadFilesSecurity = dataclasses.field() - - @dataclasses.dataclass class DownloadFilesResponse: content_type: str = dataclasses.field() status_code: int = dataclasses.field() + raw_response: Optional[requests_http.Response] = dataclasses.field(default=None) \ No newline at end of file diff --git a/files/src/codat/models/operations/list_files.py b/files/src/codat/models/operations/list_files.py index 2cf271938..099fbda1a 100755 --- a/files/src/codat/models/operations/list_files.py +++ b/files/src/codat/models/operations/list_files.py @@ -1,6 +1,7 @@ from __future__ import annotations import dataclasses import dateutil.parser +import requests as requests_http from codat import utils from dataclasses_json import Undefined, dataclass_json from datetime import datetime @@ -8,29 +9,18 @@ from typing import Optional -@dataclasses.dataclass -class ListFilesPathParams: - company_id: str = dataclasses.field(metadata={'path_param': { 'field_name': 'companyId', 'style': 'simple', 'explode': False }}) - - -@dataclasses.dataclass -class ListFilesSecurity: - api_key: str = dataclasses.field(metadata={'security': { 'scheme': True, 'type': 'apiKey', 'sub_type': 'header', 'field_name': 'Authorization' }}) - - @dataclasses.dataclass class ListFilesRequest: - path_params: ListFilesPathParams = dataclasses.field() - security: ListFilesSecurity = dataclasses.field() + company_id: str = dataclasses.field(metadata={'path_param': { 'field_name': 'companyId', 'style': 'simple', 'explode': False }}) @dataclass_json(undefined=Undefined.EXCLUDE) @dataclasses.dataclass class ListFilesFile: - display_name: Optional[str] = dataclasses.field(default=None, metadata={'dataclasses_json': { 'letter_case': utils.field_name('displayName'), 'exclude': lambda f: f is None }}) - file_name: Optional[str] = dataclasses.field(default=None, metadata={'dataclasses_json': { 'letter_case': utils.field_name('fileName'), 'exclude': lambda f: f is None }}) - source_type: Optional[str] = dataclasses.field(default=None, metadata={'dataclasses_json': { 'letter_case': utils.field_name('sourceType'), 'exclude': lambda f: f is None }}) - uploaded: Optional[datetime] = dataclasses.field(default=None, metadata={'dataclasses_json': { 'letter_case': utils.field_name('uploaded'), 'encoder': utils.datetimeisoformat(True), 'decoder': dateutil.parser.isoparse, 'mm_field': fields.DateTime(format='iso'), 'exclude': lambda f: f is None }}) + display_name: Optional[str] = dataclasses.field(default=None, metadata={'dataclasses_json': { 'letter_case': utils.get_field_name('displayName'), 'exclude': lambda f: f is None }}) + file_name: Optional[str] = dataclasses.field(default=None, metadata={'dataclasses_json': { 'letter_case': utils.get_field_name('fileName'), 'exclude': lambda f: f is None }}) + source_type: Optional[str] = dataclasses.field(default=None, metadata={'dataclasses_json': { 'letter_case': utils.get_field_name('sourceType'), 'exclude': lambda f: f is None }}) + uploaded: Optional[datetime] = dataclasses.field(default=None, metadata={'dataclasses_json': { 'letter_case': utils.get_field_name('uploaded'), 'encoder': utils.datetimeisoformat(True), 'decoder': dateutil.parser.isoparse, 'mm_field': fields.DateTime(format='iso'), 'exclude': lambda f: f is None }}) @dataclasses.dataclass @@ -38,4 +28,5 @@ class ListFilesResponse: content_type: str = dataclasses.field() status_code: int = dataclasses.field() files: Optional[list[ListFilesFile]] = dataclasses.field(default=None) + raw_response: Optional[requests_http.Response] = dataclasses.field(default=None) \ No newline at end of file diff --git a/files/src/codat/models/operations/upload_files.py b/files/src/codat/models/operations/upload_files.py index 700f87169..97f375f09 100755 --- a/files/src/codat/models/operations/upload_files.py +++ b/files/src/codat/models/operations/upload_files.py @@ -1,27 +1,18 @@ from __future__ import annotations import dataclasses - +import requests as requests_http +from typing import Optional @dataclasses.dataclass -class UploadFilesPathParams: +class UploadFilesRequest: company_id: str = dataclasses.field(metadata={'path_param': { 'field_name': 'companyId', 'style': 'simple', 'explode': False }}) connection_id: str = dataclasses.field(metadata={'path_param': { 'field_name': 'connectionId', 'style': 'simple', 'explode': False }}) -@dataclasses.dataclass -class UploadFilesSecurity: - api_key: str = dataclasses.field(metadata={'security': { 'scheme': True, 'type': 'apiKey', 'sub_type': 'header', 'field_name': 'Authorization' }}) - - -@dataclasses.dataclass -class UploadFilesRequest: - path_params: UploadFilesPathParams = dataclasses.field() - security: UploadFilesSecurity = dataclasses.field() - - @dataclasses.dataclass class UploadFilesResponse: content_type: str = dataclasses.field() status_code: int = dataclasses.field() + raw_response: Optional[requests_http.Response] = dataclasses.field(default=None) \ No newline at end of file diff --git a/files/src/codat/models/shared/__init__.py b/files/src/codat/models/shared/__init__.py index b6972b92a..ade20fff1 100755 --- a/files/src/codat/models/shared/__init__.py +++ b/files/src/codat/models/shared/__init__.py @@ -1,3 +1,3 @@ from .security import * -__all__ = ["Security"] \ No newline at end of file +__all__ = ["Security"] diff --git a/files/src/codat/models/shared/security.py b/files/src/codat/models/shared/security.py index 7fe4a9463..72db7f899 100755 --- a/files/src/codat/models/shared/security.py +++ b/files/src/codat/models/shared/security.py @@ -2,8 +2,7 @@ import dataclasses - @dataclasses.dataclass class Security: - api_key: str = dataclasses.field(metadata={'security': { 'scheme': True, 'type': 'apiKey', 'sub_type': 'header', 'field_name': 'Authorization' }}) + auth_header: str = dataclasses.field(metadata={'security': { 'scheme': True, 'type': 'apiKey', 'sub_type': 'header', 'field_name': 'Authorization' }}) \ No newline at end of file diff --git a/files/src/codat/sdk.py b/files/src/codat/sdk.py index df427b48f..f5fdffbbd 100755 --- a/files/src/codat/sdk.py +++ b/files/src/codat/sdk.py @@ -1,5 +1,11 @@ +__doc__ = """ SDK Documentation: An API for uploading and downloading files from 'File Upload' Integrations. -import requests +The Accounting file upload, Banking file upload, and Business documents file upload integrations provide simple file upload functionality. + +[Read more...](https://docs.codat.io/other/file-upload) + +[See our OpenAPI spec](https://github.com/codatio/oas) """ +import requests as requests_http from . import utils from .files import Files from codat.models import shared @@ -8,50 +14,47 @@ "https://api.codat.io", ] - class Codat: + r"""SDK Documentation: An API for uploading and downloading files from 'File Upload' Integrations. + The Accounting file upload, Banking file upload, and Business documents file upload integrations provide simple file upload functionality. + + [Read more...](https://docs.codat.io/other/file-upload) + + [See our OpenAPI spec](https://github.com/codatio/oas) """ files: Files - - _client: requests.Session - _security_client: requests.Session - _security: shared.Security + + _client: requests_http.Session + _security_client: requests_http.Session _server_url: str = SERVERS[0] _language: str = "python" - _sdk_version: str = "0.1.1" - _gen_version: str = "1.7.1" - - def __init__(self) -> None: - self._client = requests.Session() - self._security_client = requests.Session() - self._init_sdks() - - - def config_server_url(self, server_url: str, params: dict[str, str]): - if params is not None: - self._server_url = utils.replace_parameters(server_url, params) - else: - self._server_url = server_url - - self._init_sdks() - + _sdk_version: str = "0.4.0" + _gen_version: str = "1.11.0" - def config_client(self, client: requests.Session): - self._client = client + def __init__(self, + security: shared.Security = None, + server_url: str = None, + url_params: dict[str, str] = None, + client: requests_http.Session = None + ) -> None: + self._client = requests_http.Session() - if self._security is not None: - self._security_client = utils.configure_security_client(self._client, self._security) - self._init_sdks() - + + if server_url is not None: + if url_params is not None: + self._server_url = utils.template_url(server_url, url_params) + else: + self._server_url = server_url - def config_security(self, security: shared.Security): - self._security = security + if client is not None: + self._client = client + self._security_client = utils.configure_security_client(self._client, security) + + self._init_sdks() - def _init_sdks(self): - self.files = Files( self._client, self._security_client, @@ -60,5 +63,5 @@ def _init_sdks(self): self._sdk_version, self._gen_version ) - + \ No newline at end of file diff --git a/files/src/codat/utils/retries.py b/files/src/codat/utils/retries.py index 983d0d462..515316ba8 100755 --- a/files/src/codat/utils/retries.py +++ b/files/src/codat/utils/retries.py @@ -50,67 +50,67 @@ def __init__(self, inner: Exception): self.inner = inner -def retry(fn, retries: Retries): +def retry(func, retries: Retries): if retries.config.strategy == 'backoff': def do_request(): res: requests.Response try: - res = fn() + res = func() for code in retries.status_codes: if "X" in code.upper(): - codeRange = int(code[0]) + code_range = int(code[0]) - s = res.status_code / 100 + status_major = res.status_code / 100 - if s >= codeRange and s < codeRange + 1: + if status_major >= code_range and status_major < code_range + 1: raise TemporaryError(res) else: parsed_code = int(code) if res.status_code == parsed_code: raise TemporaryError(res) - except requests.exceptions.ConnectionError as e: + except requests.exceptions.ConnectionError as exception: if not retries.config.config.retry_connection_errors: raise - else: - raise PermanentError(e) - except requests.exceptions.Timeout as e: + + raise PermanentError(exception) from exception + except requests.exceptions.Timeout as exception: if not retries.config.config.retry_connection_errors: raise - else: - raise PermanentError(e) + + raise PermanentError(exception) from exception except TemporaryError: raise - except Exception as e: - raise PermanentError(e) + except Exception as exception: + raise PermanentError(exception) from exception return res return retry_with_backoff(do_request, retries.config.backoff.initial_interval, retries.config.backoff.max_interval, retries.config.backoff.exponent, retries.config.backoff.max_elapsed_time) - else: - fn() + + return func() -def retry_with_backoff(fn, initial_interval=500, max_interval=60000, exponent=1.5, max_elapsed_time=3600000): +def retry_with_backoff(func, initial_interval=500, max_interval=60000, exponent=1.5, max_elapsed_time=3600000): start = round(time.time()*1000) - x = 0 + retries = 0 while True: try: - return fn() - except PermanentError as e: - raise e.inner - except Exception as e: + return func() + except PermanentError as exception: + raise exception.inner + except Exception as exception: # pylint: disable=broad-exception-caught now = round(time.time()*1000) if now - start > max_elapsed_time: - if isinstance(e, TemporaryError): - return e.response - else: - raise + if isinstance(exception, TemporaryError): + return exception.response + + raise sleep = ((initial_interval/1000) * - exponent**x + random.uniform(0, 1)) + exponent**retries + random.uniform(0, 1)) if sleep > max_interval/1000: sleep = max_interval/1000 time.sleep(sleep) - x += 1 + retries += 1 diff --git a/files/src/codat/utils/utils.py b/files/src/codat/utils/utils.py index e9bf17b97..10c6d4f92 100755 --- a/files/src/codat/utils/utils.py +++ b/files/src/codat/utils/utils.py @@ -5,7 +5,7 @@ from datetime import date, datetime from email.message import Message from enum import Enum -from typing import Callable, Optional, Tuple, Union, get_args, get_origin +from typing import Any, Callable, Optional, Tuple, Union, get_args, get_origin from xmlrpc.client import boolean import dateutil.parser @@ -30,6 +30,9 @@ def request(self, method, url, **kwargs): def configure_security_client(client: requests.Session, security: dataclass): client = SecurityClient(client) + if security is None: + return client + sec_fields: Tuple[Field, ...] = fields(security) for sec_field in sec_fields: value = getattr(security, sec_field.name) @@ -42,7 +45,7 @@ def configure_security_client(client: requests.Session, security: dataclass): if metadata.get('option'): _parse_security_option(client, value) return client - elif metadata.get('scheme'): + if metadata.get('scheme'): # Special case for basic auth which could be a flattened struct if metadata.get("sub_type") == "basic" and not is_dataclass(value): _parse_security_scheme(client, metadata, security) @@ -136,62 +139,73 @@ def _parse_basic_auth_scheme(client: SecurityClient, scheme: dataclass): client.client.headers['Authorization'] = f'Basic {base64.b64encode(data).decode()}' -def generate_url(server_url: str, path: str, path_params: dataclass) -> str: - path_param_fields: Tuple[Field, ...] = fields(path_params) - for f in path_param_fields: - param_metadata = f.metadata.get('path_param') +def generate_url(clazz: type, server_url: str, path: str, path_params: dataclass, gbls: dict[str, dict[str, dict[str, Any]]] = None) -> str: + path_param_fields: Tuple[Field, ...] = fields(clazz) + for field in path_param_fields: + request_metadata = field.metadata.get('request') + if request_metadata is not None: + continue + + param_metadata = field.metadata.get('path_param') if param_metadata is None: continue + if param_metadata.get('style', 'simple') == 'simple': - param = getattr(path_params, f.name) + param = getattr( + path_params, field.name) if path_params is not None else None + param = _populate_from_globals( + field.name, param, 'pathParam', gbls) + if param is None: continue - if type(param) is list: + if isinstance(param, list): pp_vals: list[str] = [] for pp_val in param: if pp_val is None: continue - pp_vals.append(val_to_string(pp_val)) + pp_vals.append(_val_to_string(pp_val)) path = path.replace( - '{' + param_metadata.get('field_name', f.name) + '}', ",".join(pp_vals), 1) - elif type(param) is dict: + '{' + param_metadata.get('field_name', field.name) + '}', ",".join(pp_vals), 1) + elif isinstance(param, dict): pp_vals: list[str] = [] for pp_key in param: if param[pp_key] is None: continue if param_metadata.get('explode'): pp_vals.append( - f"{pp_key}={val_to_string(param[pp_key])}") + f"{pp_key}={_val_to_string(param[pp_key])}") else: pp_vals.append( - f"{pp_key},{val_to_string(param[pp_key])}") + f"{pp_key},{_val_to_string(param[pp_key])}") path = path.replace( - '{' + param_metadata.get('field_name', f.name) + '}', ",".join(pp_vals), 1) + '{' + param_metadata.get('field_name', field.name) + '}', ",".join(pp_vals), 1) elif not isinstance(param, (str, int, float, complex, bool)): pp_vals: list[str] = [] param_fields: Tuple[Field, ...] = fields(param) - for field in param_fields: - param_value_metadata = field.metadata.get('path_param') + for param_field in param_fields: + param_value_metadata = param_field.metadata.get( + 'path_param') if not param_value_metadata: continue - parm_name = param_value_metadata.get('field_name', f.name) + parm_name = param_value_metadata.get( + 'field_name', field.name) - param_field_val = getattr(param, field.name) + param_field_val = getattr(param, param_field.name) if param_field_val is None: continue - elif param_metadata.get('explode'): + if param_metadata.get('explode'): pp_vals.append( - f"{parm_name}={val_to_string(param_field_val)}") + f"{parm_name}={_val_to_string(param_field_val)}") else: pp_vals.append( - f"{parm_name},{val_to_string(param_field_val)}") + f"{parm_name},{_val_to_string(param_field_val)}") path = path.replace( - '{' + param_metadata.get('field_name', f.name) + '}', ",".join(pp_vals), 1) + '{' + param_metadata.get('field_name', field.name) + '}', ",".join(pp_vals), 1) else: path = path.replace( - '{' + param_metadata.get('field_name', f.name) + '}', val_to_string(param), 1) + '{' + param_metadata.get('field_name', field.name) + '}', _val_to_string(param), 1) return server_url.removesuffix("/") + path @@ -200,40 +214,46 @@ def is_optional(field): return get_origin(field) is Union and type(None) in get_args(field) -def replace_parameters(string_with_params: str, params: dict[str, str]) -> str: +def template_url(url_with_params: str, params: dict[str, str]) -> str: for key, value in params.items(): - string_with_params = string_with_params.replace( + url_with_params = url_with_params.replace( '{' + key + '}', value) - return string_with_params + return url_with_params -def get_query_params(query_params: dataclass) -> dict[str, list[str]]: - if query_params is None: - return {} - +def get_query_params(clazz: type, query_params: dataclass, gbls: dict[str, dict[str, dict[str, Any]]] = None) -> dict[str, list[str]]: params: dict[str, list[str]] = {} - param_fields: Tuple[Field, ...] = fields(query_params) - for f in param_fields: - metadata = f.metadata.get('query_param') + param_fields: Tuple[Field, ...] = fields(clazz) + for field in param_fields: + request_metadata = field.metadata.get('request') + if request_metadata is not None: + continue + + metadata = field.metadata.get('query_param') if not metadata: continue - param_name = f.name + param_name = field.name + value = getattr( + query_params, param_name) if query_params is not None else None + + value = _populate_from_globals(param_name, value, 'queryParam', gbls) + f_name = metadata.get("field_name") serialization = metadata.get('serialization', '') if serialization != '': params = params | _get_serialized_query_params( - metadata, f_name, getattr(query_params, param_name)) + metadata, f_name, value) else: style = metadata.get('style', 'form') if style == 'deepObject': params = params | _get_deep_object_query_params( - metadata, f_name, getattr(query_params, param_name)) + metadata, f_name, value) elif style == 'form': params = params | _get_form_query_params( - metadata, f_name, getattr(query_params, param_name)) + metadata, f_name, value) else: raise Exception('not yet implemented') return params @@ -246,16 +266,16 @@ def get_headers(headers_params: dataclass) -> dict[str, str]: headers: dict[str, str] = {} param_fields: Tuple[Field, ...] = fields(headers_params) - for f in param_fields: - metadata = f.metadata.get('header') + for field in param_fields: + metadata = field.metadata.get('header') if not metadata: continue value = _serialize_header(metadata.get( - 'explode', False), getattr(headers_params, f.name)) + 'explode', False), getattr(headers_params, field.name)) if value != '': - headers[metadata.get('field_name', f.name)] = value + headers[metadata.get('field_name', field.name)] = value return headers @@ -283,13 +303,13 @@ def _get_deep_object_query_params(metadata: dict, field_name: str, obj: any) -> if not obj_param_metadata: continue - val = getattr(obj, obj_field.name) - if val is None: + obj_val = getattr(obj, obj_field.name) + if obj_val is None: continue - if isinstance(val, list): - for v in val: - if v is None: + if isinstance(obj_val, list): + for val in obj_val: + if val is None: continue if params.get(f'{metadata.get("field_name", field_name)}[{obj_param_metadata.get("field_name", obj_field.name)}]') is None: @@ -297,11 +317,11 @@ def _get_deep_object_query_params(metadata: dict, field_name: str, obj: any) -> ] params[ - f'{metadata.get("field_name", field_name)}[{obj_param_metadata.get("field_name", obj_field.name)}]'].append(val_to_string(v)) + f'{metadata.get("field_name", field_name)}[{obj_param_metadata.get("field_name", obj_field.name)}]'].append(_val_to_string(val)) else: params[ f'{metadata.get("field_name", field_name)}[{obj_param_metadata.get("field_name", obj_field.name)}]'] = [ - val_to_string(val)] + _val_to_string(obj_val)] elif isinstance(obj, dict): for key, value in obj.items(): if value is None: @@ -317,10 +337,10 @@ def _get_deep_object_query_params(metadata: dict, field_name: str, obj: any) -> ] params[ - f'{metadata.get("field_name", field_name)}[{key}]'].append(val_to_string(val)) + f'{metadata.get("field_name", field_name)}[{key}]'].append(_val_to_string(val)) else: params[f'{metadata.get("field_name", field_name)}[{key}]'] = [ - val_to_string(value)] + _val_to_string(value)] return params @@ -337,46 +357,44 @@ def _get_form_query_params(metadata: dict, field_name: str, obj: any) -> dict[st return _populate_form(field_name, metadata.get("explode", True), obj, _get_query_param_field_name) -def serialize_request_body(request: dataclass) -> Tuple[str, any, any]: +SERIALIZATION_METHOD_TO_CONTENT_TYPE = { + 'json': 'application/json', + 'form': 'application/x-www-form-urlencoded', + 'multipart': 'multipart/form-data', + 'raw': 'application/octet-stream', + 'string': 'text/plain', +} + + +def serialize_request_body(request: dataclass, request_field_name: str, serialization_method: str) -> Tuple[str, any, any]: if request is None: return None, None, None, None - request_val = getattr(request, "request") - if request_val is None: - raise Exception("request body not found") + if not is_dataclass(request) or not hasattr(request, request_field_name): + return serialize_content_type(request_field_name, SERIALIZATION_METHOD_TO_CONTENT_TYPE[serialization_method], request) + + request_val = getattr(request, request_field_name) request_fields: Tuple[Field, ...] = fields(request) request_metadata = None - for f in request_fields: - if f.name == "request": - request_metadata = f.metadata.get('request') + for field in request_fields: + if field.name == request_field_name: + request_metadata = field.metadata.get('request') break - if request_metadata is not None: - # single request - return serialize_content_type('request', request_metadata.get('media_type', 'application/octet-stream'), request_val) - - request_fields: Tuple[Field, ...] = fields(request_val) - for f in request_fields: - req = getattr(request_val, f.name) - if req is None: - continue - - request_metadata = f.metadata.get('request') - if request_metadata is None: - raise Exception( - f'missing request tag on request body field {f.name}') + if request_metadata is None: + raise Exception('invalid request type') - return serialize_content_type(f.name, request_metadata.get('media_type', 'application/octet-stream'), req) + return serialize_content_type(request_field_name, request_metadata.get('media_type', 'application/octet-stream'), request_val) def serialize_content_type(field_name: str, media_type: str, request: dataclass) -> Tuple[str, any, list[list[any]]]: - if re.match(r'(application|text)\/.*?\+*json.*', media_type) != None: + if re.match(r'(application|text)\/.*?\+*json.*', media_type) is not None: return media_type, marshal_json(request), None - if re.match(r'multipart\/.*', media_type) != None: + if re.match(r'multipart\/.*', media_type) is not None: return serialize_multipart_form(media_type, request) - if re.match(r'application\/x-www-form-urlencoded.*', media_type) != None: + if re.match(r'application\/x-www-form-urlencoded.*', media_type) is not None: return media_type, serialize_form_data(field_name, request), None if isinstance(request, (bytes, bytearray)): return media_type, request, None @@ -434,9 +452,9 @@ def serialize_multipart_form(media_type: str, request: dataclass) -> Tuple[str, if value is None: continue form.append( - [field_name + "[]", [None, val_to_string(value)]]) + [field_name + "[]", [None, _val_to_string(value)]]) else: - form.append([field_name, [None, val_to_string(val)]]) + form.append([field_name, [None, _val_to_string(val)]]) return media_type, None, form @@ -446,15 +464,15 @@ def serialize_dict(original: dict, explode: bool, field_name, existing: Optional existing = [] if explode is True: - for k, v in original.items(): - if k not in existing: - existing[k] = [] - existing[k].append(v) + for key, val in original.items(): + if key not in existing: + existing[key] = [] + existing[key].append(val) else: temp = [] - for k, v in original.items(): - temp.append(str(k)) - temp.append(str(v)) + for key, val in original.items(): + temp.append(str(key)) + temp.append(str(val)) if field_name not in existing: existing[field_name] = [] existing[field_name].append(",".join(temp)) @@ -487,7 +505,7 @@ def serialize_form_data(field_name: str, data: dataclass) -> dict[str, any]: f'Invalid form style for field {field.name}') elif isinstance(data, dict): for key, value in data.items(): - form[key] = [val_to_string(value)] + form[key] = [_val_to_string(value)] else: raise Exception(f'Invalid request body type for field {field_name}') @@ -504,7 +522,7 @@ def _get_form_field_name(obj_field: Field) -> str: def _populate_form(field_name: str, explode: boolean, obj: any, get_field_name_func: Callable) -> dict[str, list[str]]: - params: dict[str, str | list[str]] = {} + params: dict[str, list[str]] = {} if obj is None: return params @@ -523,10 +541,10 @@ def _populate_form(field_name: str, explode: boolean, obj: any, get_field_name_f continue if explode: - params[obj_field_name] = [val_to_string(val)] + params[obj_field_name] = [_val_to_string(val)] else: items.append( - f'{obj_field_name},{val_to_string(val)}') + f'{obj_field_name},{_val_to_string(val)}') if len(items) > 0: params[field_name] = [','.join(items)] @@ -537,9 +555,9 @@ def _populate_form(field_name: str, explode: boolean, obj: any, get_field_name_f continue if explode: - params[key] = val_to_string(value) + params[key] = _val_to_string(value) else: - items.append(f'{key},{val_to_string(value)}') + items.append(f'{key},{_val_to_string(value)}') if len(items) > 0: params[field_name] = [','.join(items)] @@ -553,19 +571,19 @@ def _populate_form(field_name: str, explode: boolean, obj: any, get_field_name_f if explode: if not field_name in params: params[field_name] = [] - params[field_name].append(val_to_string(value)) + params[field_name].append(_val_to_string(value)) else: - items.append(val_to_string(value)) + items.append(_val_to_string(value)) if len(items) > 0: params[field_name] = [','.join([str(item) for item in items])] else: - params[field_name] = val_to_string(obj) + params[field_name] = [_val_to_string(obj)] return params -def _serialize_header(explode: boolean, obj: any) -> str: +def _serialize_header(explode: bool, obj: any) -> str: if obj is None: return '' @@ -589,10 +607,10 @@ def _serialize_header(explode: boolean, obj: any) -> str: if explode: items.append( - f'{obj_field_name}={val_to_string(val)}') + f'{obj_field_name}={_val_to_string(val)}') else: items.append(obj_field_name) - items.append(val_to_string(val)) + items.append(_val_to_string(val)) if len(items) > 0: return ','.join(items) @@ -604,10 +622,10 @@ def _serialize_header(explode: boolean, obj: any) -> str: continue if explode: - items.append(f'{key}={val_to_string(value)}') + items.append(f'{key}={_val_to_string(value)}') else: items.append(key) - items.append(val_to_string(value)) + items.append(_val_to_string(value)) if len(items) > 0: return ','.join([str(item) for item in items]) @@ -618,83 +636,98 @@ def _serialize_header(explode: boolean, obj: any) -> str: if value is None: continue - items.append(val_to_string(value)) + items.append(_val_to_string(value)) - return ','.join(items) + if len(items) > 0: + return ','.join(items) else: - return f'{val_to_string(obj)}' + return f'{_val_to_string(obj)}' + + return '' -def unmarshal_json(data, t): - Unmarhsal = make_dataclass('Unmarhsal', [('res', t)], +def unmarshal_json(data, typ): + unmarhsal = make_dataclass('Unmarhsal', [('res', typ)], bases=(DataClassJsonMixin,)) - d = json.loads(data) - out = Unmarhsal.from_dict({"res": d}) + json_dict = json.loads(data) + out = unmarhsal.from_dict({"res": json_dict}) return out.res -def marshal_json(c): - Marshal = make_dataclass('Marshal', [('res', type(c))], +def marshal_json(val): + marshal = make_dataclass('Marshal', [('res', type(val))], bases=(DataClassJsonMixin,)) - m = Marshal(res=c) - d = m.to_dict() - return json.dumps(d["res"]) + marshaller = marshal(res=val) + json_dict = marshaller.to_dict() + return json.dumps(json_dict["res"]) def match_content_type(content_type: str, pattern: str) -> boolean: - if content_type == pattern or pattern == "*" or pattern == "*/*": + if pattern in (content_type, "*", "*/*"): return True - m = Message() - m['content-type'] = content_type - media_type = m.get_content_type() + msg = Message() + msg['content-type'] = content_type + media_type = msg.get_content_type() if media_type == pattern: return True parts = media_type.split("/") if len(parts) == 2: - if f'{parts[0]}/*' == pattern or f'*/{parts[1]}' == pattern: + if pattern in (f'{parts[0]}/*', f'*/{parts[1]}'): return True return False def datetimeisoformat(optional: bool): - def isoformatoptional(v): - if optional and v is None: + def isoformatoptional(val): + if optional and val is None: return None - return val_to_string(v) + return _val_to_string(val) return isoformatoptional def dateisoformat(optional: bool): - def isoformatoptional(v): - if optional and v is None: + def isoformatoptional(val): + if optional and val is None: return None - return date.isoformat(v) + return date.isoformat(val) return isoformatoptional -def datefromisoformat(date: str): - return dateutil.parser.parse(date).date() +def datefromisoformat(date_str: str): + return dateutil.parser.parse(date_str).date() -def field_name(name): +def get_field_name(name): def override(_, _field_name=name): return _field_name return override -def val_to_string(val): +def _val_to_string(val): if isinstance(val, bool): return str(val).lower() - elif isinstance(val, datetime): + if isinstance(val, datetime): return val.isoformat().replace('+00:00', 'Z') - elif isinstance(val, Enum): + if isinstance(val, Enum): return val.value return str(val) + + +def _populate_from_globals(param_name: str, value: any, param_type: str, gbls: dict[str, dict[str, dict[str, Any]]]): + if value is None and gbls is not None: + if 'parameters' in gbls: + if param_type in gbls['parameters']: + if param_name in gbls['parameters'][param_type]: + global_value = gbls['parameters'][param_type][param_name] + if global_value is not None: + value = global_value + + return value