diff --git a/poetry.lock b/poetry.lock index 034f71968..ea0b0a957 100644 --- a/poetry.lock +++ b/poetry.lock @@ -26,7 +26,7 @@ python-versions = ">=3.5" dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] -tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] +tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] [[package]] name = "Automat" @@ -143,7 +143,7 @@ optional = false python-versions = ">=3.6.0" [package.extras] -unicode_backport = ["unicodedata2"] +unicode-backport = ["unicodedata2"] [[package]] name = "cibuildwheel" @@ -382,9 +382,9 @@ python-versions = ">=3.6.1,<4.0" [package.extras] colors = ["colorama (>=0.4.3,<0.5.0)"] -pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +pipfile-deprecated-finder = ["pipreqs", "requirementslib"] plugins = ["setuptools"] -requirements_deprecated_finder = ["pip-api", "pipreqs"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] [[package]] name = "jaraco.classes" @@ -761,6 +761,17 @@ python-versions = ">=3.6" [package.dependencies] pytest = ">=5.0.0" +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + [[package]] name = "pywin32-ctypes" version = "0.2.0" @@ -801,7 +812,7 @@ urllib3 = ">=1.21.1,<1.27" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-toolbelt" @@ -961,20 +972,20 @@ typing-extensions = ">=3.6.5" "zope.interface" = ">=4.4.2" [package.extras] -all_non_platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyopenssl (>=16.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] +all-non-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyopenssl (>=16.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "cryptography (>=2.6)", "pyasn1"] -conch_nacl = ["PyNaCl", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "cryptography (>=2.6)", "pyasn1"] +conch-nacl = ["PyNaCl", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "cryptography (>=2.6)", "pyasn1"] contextvars = ["contextvars (>=2.4,<3)"] dev = ["coverage (>=6b1,<7)", "pydoctor (>=21.9.0,<21.10.0)", "pyflakes (>=2.2,<3.0)", "python-subunit (>=1.4,<2.0)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "sphinx (>=4.1.2,<6)", "sphinx-rtd-theme (>=0.5,<1.0)", "towncrier (>=19.2,<20.0)", "twistedchecker (>=0.7,<1.0)"] -dev_release = ["pydoctor (>=21.9.0,<21.10.0)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "sphinx (>=4.1.2,<6)", "sphinx-rtd-theme (>=0.5,<1.0)", "towncrier (>=19.2,<20.0)"] +dev-release = ["pydoctor (>=21.9.0,<21.10.0)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "sphinx (>=4.1.2,<6)", "sphinx-rtd-theme (>=0.5,<1.0)", "towncrier (>=19.2,<20.0)"] http2 = ["h2 (>=3.0,<5.0)", "priority (>=1.1.0,<2.0)"] -macos_platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyobjc-core", "pyobjc-framework-CFNetwork", "pyobjc-framework-Cocoa", "pyopenssl (>=16.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] +macos-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyobjc-core", "pyobjc-framework-CFNetwork", "pyobjc-framework-Cocoa", "pyopenssl (>=16.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] mypy = ["PyHamcrest (>=1.9.0)", "PyNaCl", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "coverage (>=6b1,<7)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "mypy (==0.930)", "mypy-zope (==0.3.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pydoctor (>=21.9.0,<21.10.0)", "pyflakes (>=2.2,<3.0)", "pyopenssl (>=16.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "service-identity (>=18.1.0)", "sphinx (>=4.1.2,<6)", "sphinx-rtd-theme (>=0.5,<1.0)", "towncrier (>=19.2,<20.0)", "twistedchecker (>=0.7,<1.0)", "types-pyOpenSSL", "types-setuptools"] -osx_platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyobjc-core", "pyobjc-framework-CFNetwork", "pyobjc-framework-Cocoa", "pyopenssl (>=16.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] +osx-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyobjc-core", "pyobjc-framework-CFNetwork", "pyobjc-framework-Cocoa", "pyopenssl (>=16.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] serial = ["pyserial (>=3.0)", "pywin32 (!=226)"] test = ["PyHamcrest (>=1.9.0)", "cython-test-exception-raiser (>=1.0.2,<2)"] tls = ["idna (>=2.4)", "pyopenssl (>=16.0.0)", "service-identity (>=18.1.0)"] -windows_platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyopenssl (>=16.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] +windows-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyopenssl (>=16.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] [[package]] name = "twisted-iocpsupport" @@ -1072,7 +1083,7 @@ opentelemetry = ["opentelemetry-api", "opentelemetry-sdk"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "2de30dab6de94505d43b59178d6abba15ba69898c9c31bbe238eafda50515526" +content-hash = "d9fa29fbb571c4c1f1517360256e1f9fa7df1d48c77f46b23fd9980ecd5d675b" [metadata.files] appdirs = [ @@ -1631,6 +1642,10 @@ pytest-timeout = [ {file = "pytest-timeout-2.1.0.tar.gz", hash = "sha256:c07ca07404c612f8abbe22294b23c368e2e5104b521c1790195561f37e1ac3d9"}, {file = "pytest_timeout-2.1.0-py3-none-any.whl", hash = "sha256:f6f50101443ce70ad325ceb4473c4255e9d74e3c7cd0ef827309dfa4c0d975c6"}, ] +python-dateutil = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] pywin32-ctypes = [ {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, diff --git a/pyproject.toml b/pyproject.toml index f85ac38b3..98025e289 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,20 @@ [tool.poetry] -name = "temporalio" -version = "0.1b2" +authors = ["Temporal Technologies Inc "] description = "Temporal.io Python SDK" +documentation = "https://docs.temporal.io/docs/python" +homepage = "https://github.com/temporalio/sdk-python" +keywords = ["temporal", "workflow"] license = "MIT" -authors = ["Temporal Technologies Inc "] +name = "temporalio" readme = "README.md" -homepage = "https://github.com/temporalio/sdk-python" repository = "https://github.com/temporalio/sdk-python" -documentation = "https://docs.temporal.io/docs/python" -keywords = ["temporal", "workflow"] +version = "0.1b2" # We need to include proto source that is otherwise excluded via .gitignore. # We have chosen to keep all source including Rust source in precompiled wheels # for easy viewing. It is also complicated to exclude certain pieces for wheels # with Poetry (see https://github.com/python-poetry/poetry/issues/3380). -include = ["temporalio/api/**/*", "temporalio/bridge/proto/**/*"] exclude = ["temporalio/bridge/**/target"] +include = ["temporalio/api/**/*", "temporalio/bridge/proto/**/*"] # Known undocumented API for hooking into setup. Unfortunately Poetry does not # support this script in a subdirectory like scripts/. @@ -25,11 +25,12 @@ script = "build.py" "Bug Tracker" = "https://github.com/temporalio/sdk-python/issues" [tool.poetry.dependencies] -grpcio = { version = "^1.48.0", optional = true } -opentelemetry-api = { version = "^1.11.1", optional = true } -opentelemetry-sdk = { version = "^1.11.1", optional = true } +grpcio = {version = "^1.48.0", optional = true} +opentelemetry-api = {version = "^1.11.1", optional = true} +opentelemetry-sdk = {version = "^1.11.1", optional = true} protobuf = "~4.21" python = "^3.7" +python-dateutil = "^2.8.2" types-protobuf = "~3.20" typing-extensions = "^4.2.0" @@ -45,7 +46,7 @@ pydantic = "^1.9.1" pydocstyle = "^6.1.1" # TODO(cretz): Update when https://github.com/twisted/pydoctor/pull/595 released # pydoctor = "^22.5.1" -pydoctor = { git = "https://github.com/cretz/pydoctor.git", branch = "overloads" } +pydoctor = {git = "https://github.com/cretz/pydoctor.git", branch = "overloads"} pytest = "^7.1.2" pytest-asyncio = "^0.18.3" pytest-timeout = "^2.1.0" @@ -55,12 +56,12 @@ toml = "^0.10.2" twine = "^4.0.1" [tool.poetry.extras] -opentelemetry = ["opentelemetry-api", "opentelemetry-sdk"] grpc = ["grpc"] +opentelemetry = ["opentelemetry-api", "opentelemetry-sdk"] [tool.poe.tasks] -build-develop = ["build-bridge-develop"] build-bridge-develop = "python scripts/setup_bridge.py develop" +build-develop = ["build-bridge-develop"] fix-wheel = "python scripts/fix_wheel.py" format = [{cmd = "black ."}, {cmd = "isort ."}] gen-docs = "pydoctor" @@ -87,15 +88,15 @@ ignore_fail = "return_non_zero" cmd = "pip install --no-index --find-links=./dist temporalio" [[tool.poe.tasks.test-dist-single.sequence]] cmd = "pytest -k test_activity_hello" -env = { TEMPORAL_INTEGRATION_TEST = "1" } +env = {TEMPORAL_INTEGRATION_TEST = "1"} [[tool.poe.tasks.test-dist-single.sequence]] cmd = "pip uninstall temporalio -y" [tool.pytest.ini_options] asyncio_mode = "auto" log_cli = true -log_cli_level = "INFO" log_cli_format = "%(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)" +log_cli_level = "INFO" testpaths = ["tests"] timeout = 600 timeout_func_only = true @@ -108,7 +109,7 @@ build-verbosity = "1" [tool.cibuildwheel.linux] before-all = "curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain stable -y && yum install -y openssl-devel" -environment = { PATH = "$PATH:$HOME/.cargo/bin", CARGO_NET_GIT_FETCH_WITH_CLI = "true" } +environment = {PATH = "$PATH:$HOME/.cargo/bin", CARGO_NET_GIT_FETCH_WITH_CLI = "true"} [[tool.cibuildwheel.overrides]] # We need the aarch64 target for Rust @@ -120,22 +121,23 @@ profile = "black" skip_gitignore = true [tool.mypy] -ignore_missing_imports = true exclude = [ # Ignore generated code 'temporalio/api', 'temporalio/bridge/proto', ] +ignore_missing_imports = true [tool.pydocstyle] convention = "google" # https://github.com/PyCQA/pydocstyle/issues/363#issuecomment-625563088 -match_dir = "^(?!(docs|scripts|tests|api|proto|\\.)).*" add_ignore = [ # We like to wrap at a certain number of chars, even long summary sentences. # https://github.com/PyCQA/pydocstyle/issues/184 - "D205", "D415" + "D205", + "D415", ] +match_dir = "^(?!(docs|scripts|tests|api|proto|\\.)).*" [tool.pydoctor] add-package = ["temporalio"] diff --git a/temporalio/converter.py b/temporalio/converter.py index 6abebbb30..542649142 100644 --- a/temporalio/converter.py +++ b/temporalio/converter.py @@ -28,6 +28,7 @@ import google.protobuf.json_format import google.protobuf.message import google.protobuf.symbol_database +from dateutil import parser from typing_extensions import Literal import temporalio.api.common.v1 @@ -697,7 +698,7 @@ def decode_search_attributes( val = [val] # Convert each item to datetime if necessary if v.metadata.get("type") == b"Datetime": - val = [datetime.fromisoformat(v) for v in val] + val = [parser.isoparse(v) for v in val] ret[k] = val return ret diff --git a/tests/test_converter.py b/tests/test_converter.py index 683cc8ac3..4da0bfa4d 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -4,7 +4,7 @@ import sys from collections import deque from dataclasses import dataclass -from datetime import datetime +from datetime import datetime, timezone from enum import Enum, IntEnum from typing import ( Any, @@ -32,6 +32,7 @@ import temporalio.common import temporalio.converter from temporalio.api.common.v1 import Payload as AnotherNameForPayload +from temporalio.api.common.v1.message_pb2 import Payload class NonSerializableClass: @@ -164,6 +165,37 @@ def test_encode_search_attribute_values(): temporalio.converter.encode_search_attribute_values(["foo", 123]) +def test_decode_search_attributes(): + """Tests decode from protobuf for python types""" + + def payload(key, dtype, data, encoding=None): + if encoding is None: + encoding = {"encoding": b"json/plain"} + check = Payload( + data=bytes(data, encoding="utf-8"), + metadata={"type": bytes(dtype, encoding="utf-8"), **encoding}, + ) + return temporalio.api.common.v1.SearchAttributes(indexed_fields={key: check}) + + # Check basic keyword parsing works + kw_check = temporalio.converter.decode_search_attributes( + payload("kw", "Keyword", '"test-id"') + ) + assert kw_check["kw"][0] == "test-id" + + # Ensure original DT functionality works + dt_check = temporalio.converter.decode_search_attributes( + payload("dt", "Datetime", '"2020-01-01T00:00:00"') + ) + assert dt_check["dt"][0] == datetime(2020, 1, 1, 0, 0, 0) + + # Check timezone aware works as server is using ISO 8601 + dttz_check = temporalio.converter.decode_search_attributes( + payload("dt", "Datetime", '"2020-01-01T00:00:00Z"') + ) + assert dttz_check["dt"][0] == datetime(2020, 1, 1, 0, 0, 0, tzinfo=timezone.utc) + + NewIntType = NewType("NewIntType", int) MyDataClassAlias = MyDataClass