appier 1.34.12__py2.py3-none-any.whl → 1.35.1__py2.py3-none-any.whl

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.
appier/__init__.py CHANGED
@@ -144,7 +144,16 @@ from .bus import Bus, MemoryBus, RedisBus
144
144
  from .cache import Cache, MemoryCache, FileCache, RedisCache, SerializedCache
145
145
  from .component import Component
146
146
  from .compress import Compress
147
- from .config import conf, conf_prefix, conf_suffix, conf_s, conf_r, conf_d, conf_ctx
147
+ from .config import (
148
+ conf,
149
+ conf_prefix,
150
+ conf_suffix,
151
+ conf_s,
152
+ conf_r,
153
+ conf_d,
154
+ conf_ctx,
155
+ conf_override,
156
+ )
148
157
  from .controller import Controller
149
158
  from .crypt import Cipher, RC4, Spritz
150
159
  from .data import (
appier/amqp.pyi ADDED
@@ -0,0 +1,14 @@
1
+ from typing import Any
2
+
3
+ URL: str
4
+ TIMEOUT: int
5
+ connection: Any | None
6
+
7
+ class AMQP:
8
+ url: str | None
9
+ _connection: Any | None
10
+ def __init__(self, url: str | None = ...): ...
11
+ def get_connection(self, url: str | None = ..., timeout: int = ...) -> Any: ...
12
+
13
+ def get_connection(url: str = ..., timeout: int = ...) -> Any: ...
14
+ def properties(*args, **kwargs) -> Any: ...
appier/api.pyi ADDED
@@ -0,0 +1,168 @@
1
+ from typing import Any, Callable, Self
2
+
3
+ from .base import App
4
+ from .observer import Observable
5
+
6
+ class API(Observable):
7
+ SINGLETON: Self | None
8
+ owner: App
9
+ auth_callback: Callable | None
10
+ def __init__(self, owner: App = ..., *args, **kwargs): ...
11
+ @classmethod
12
+ def singleton(cls, *args, **kwargs) -> Self: ...
13
+ def get(
14
+ self,
15
+ url: str,
16
+ headers: dict[str, Any] | None = ...,
17
+ params: dict[str, Any] | None = ...,
18
+ handle: bool | None = ...,
19
+ silent: bool | None = ...,
20
+ redirect: bool | None = ...,
21
+ timeout: int | float | None = ...,
22
+ callback: bool = ...,
23
+ extra: dict[str, Any] | None = ...,
24
+ **kwargs
25
+ ) -> Any: ...
26
+ def post(
27
+ self,
28
+ url: str,
29
+ data: Any = ...,
30
+ data_j: Any = ...,
31
+ data_m: Any = ...,
32
+ headers: dict[str, Any] | None = ...,
33
+ params: dict[str, Any] | None = ...,
34
+ mime: str | None = ...,
35
+ handle: bool | None = ...,
36
+ silent: bool | None = ...,
37
+ redirect: bool | None = ...,
38
+ timeout: int | float | None = ...,
39
+ callback: bool = ...,
40
+ extra: dict[str, Any] | None = ...,
41
+ **kwargs
42
+ ) -> Any: ...
43
+ def put(
44
+ self,
45
+ url: str,
46
+ data: Any = ...,
47
+ data_j: Any = ...,
48
+ data_m: Any = ...,
49
+ headers: dict[str, Any] | None = ...,
50
+ params: dict[str, Any] | None = ...,
51
+ mime: str | None = ...,
52
+ handle: bool | None = ...,
53
+ silent: bool | None = ...,
54
+ redirect: bool | None = ...,
55
+ timeout: int | float | None = ...,
56
+ callback: bool = ...,
57
+ extra: dict[str, Any] | None = ...,
58
+ **kwargs
59
+ ) -> Any: ...
60
+ def delete(
61
+ self,
62
+ url: str,
63
+ headers: dict[str, Any] | None = ...,
64
+ params: dict[str, Any] | None = ...,
65
+ handle: bool | None = ...,
66
+ silent: bool | None = ...,
67
+ redirect: bool | None = ...,
68
+ timeout: int | float | None = ...,
69
+ callback: bool = ...,
70
+ extra: dict[str, Any] | None = ...,
71
+ **kwargs
72
+ ) -> Any: ...
73
+ def patch(
74
+ self,
75
+ url: str,
76
+ data: Any = ...,
77
+ data_j: Any = ...,
78
+ data_m: Any = ...,
79
+ headers: dict[str, Any] | None = ...,
80
+ params: dict[str, Any] | None = ...,
81
+ mime: str | None = ...,
82
+ handle: bool | None = ...,
83
+ silent: bool | None = ...,
84
+ redirect: bool | None = ...,
85
+ timeout: int | float | None = ...,
86
+ callback: bool = ...,
87
+ extra: dict[str, Any] | None = ...,
88
+ **kwargs
89
+ ) -> Any: ...
90
+ def request(self, method: Callable, *args, **kwargs) -> Any: ...
91
+ def build( # pyright: ignore[reportIncompatibleMethodOverride]
92
+ self,
93
+ method: str,
94
+ url: str,
95
+ data: Any = ...,
96
+ data_j: Any = ...,
97
+ data_m: Any = ...,
98
+ headers: dict[str, Any] | None = ...,
99
+ params: dict[str, Any] | None = ...,
100
+ mime: str | None = ...,
101
+ kwargs: dict[str, Any] | None = ...,
102
+ ) -> None: ...
103
+ def handle_error(self, error: Exception) -> None: ...
104
+ @property
105
+ def logger(self) -> Any: ...
106
+
107
+ class OAuthAPI(API):
108
+ DIRECT_MODE: int
109
+ OAUTH_MODE: int
110
+ UNSET_MODE: int
111
+ mode: int
112
+ def __init__(self, *args, **kwargs): ...
113
+ def handle_error(self, error: Exception) -> None: ...
114
+ def is_direct(self) -> bool: ...
115
+ def is_oauth(self) -> bool: ...
116
+ def _get_mode(self) -> int: ...
117
+
118
+ class OAuth1API(OAuthAPI):
119
+ oauth_token: str | None
120
+ oauth_token_secret: str | None
121
+ client_key: str | None
122
+ client_secret: str | None
123
+ def __init__(self, *args, **kwargs): ...
124
+ def build(
125
+ self,
126
+ method: str,
127
+ url: str,
128
+ data: Any = ...,
129
+ data_j: Any = ...,
130
+ data_m: Any = ...,
131
+ headers: dict[str, Any] | None = ...,
132
+ params: dict[str, Any] | None = ...,
133
+ mime: str | None = ...,
134
+ kwargs: dict[str, Any] | None = ...,
135
+ ) -> None: ...
136
+ def auth_header(
137
+ self,
138
+ method: str,
139
+ url: str,
140
+ headers: dict[str, Any],
141
+ kwargs: dict[str, Any],
142
+ sign_method: str = ...,
143
+ ) -> None: ...
144
+ @property
145
+ def auth_default(self) -> bool: ...
146
+
147
+ class OAuth2API(OAuthAPI):
148
+ access_token: str | None
149
+ def __init__(self, *args, **kwargs): ...
150
+ def build(
151
+ self,
152
+ method: str,
153
+ url: str,
154
+ data: Any = ...,
155
+ data_j: Any = ...,
156
+ data_m: Any = ...,
157
+ headers: dict[str, Any] | None = ...,
158
+ params: dict[str, Any] | None = ...,
159
+ mime: str | None = ...,
160
+ kwargs: dict[str, Any] | None = ...,
161
+ ) -> None: ...
162
+ def get_access_token(self) -> str: ...
163
+ @property
164
+ def oauth_types(self) -> tuple[str, ...]: ...
165
+ @property
166
+ def oauth_param(self) -> str: ...
167
+ @property
168
+ def token_default(self) -> bool: ...
appier/asgi.py CHANGED
@@ -189,9 +189,16 @@ class ASGIApp(object):
189
189
  self._ensure_start(ctx, start_response)
190
190
  await ctx["start_task"]
191
191
 
192
+ # ensures the result is a sequence for iteration and if it's
193
+ # not initializes it as an empty list, notice the special case
194
+ # of a string that should be converted into a single element list
195
+ result = result if result else []
196
+ if legacy.is_string(result):
197
+ result = [result]
198
+
192
199
  # iterates over the complete set of chunks in the response
193
200
  # iterator to send each of them to the client side
194
- for chunk in result if result else []:
201
+ for chunk in result:
195
202
  if asyncio.iscoroutine(chunk):
196
203
  await chunk
197
204
  elif asyncio.isfuture(chunk):
appier/asgi.pyi ADDED
@@ -0,0 +1,74 @@
1
+ from typing import Any, Callable, Coroutine
2
+
3
+ class ASGIApp:
4
+ _asgi: ASGIApp | None
5
+ server_version: str | None
6
+ _server: Any | None
7
+ @classmethod
8
+ async def asgi_entry(
9
+ cls,
10
+ scope: dict[str, Any],
11
+ receive: Callable[[], Coroutine[Any, Any, dict[str, Any]]],
12
+ send: Callable[[dict[str, Any]], Coroutine[Any, Any, None]],
13
+ ) -> None: ...
14
+ def serve_uvicorn(self, host: str, port: int, **kwargs) -> None: ...
15
+ def serve_hypercorn(
16
+ self,
17
+ host: str,
18
+ port: int,
19
+ ssl: bool = ...,
20
+ key_file: str | None = ...,
21
+ cer_file: str | None = ...,
22
+ **kwargs
23
+ ) -> None: ...
24
+ def serve_daphne(self, host: str, port: int, **kwargs) -> None: ...
25
+ async def send(self, data: Any, content_type: str | None = ...) -> Any: ...
26
+ async def app_asgi(self, *args, **kwargs) -> Any: ...
27
+ async def application_asgi(
28
+ self,
29
+ scope: dict[str, Any],
30
+ receive: Callable[[], Coroutine[Any, Any, dict[str, Any]]],
31
+ send: Callable[[dict[str, Any]], Coroutine[Any, Any, None]],
32
+ ) -> None: ...
33
+ async def asgi_lifespan(
34
+ self,
35
+ scope: dict[str, Any],
36
+ receive: Callable[[], Coroutine[Any, Any, dict[str, Any]]],
37
+ send: Callable[[dict[str, Any]], Coroutine[Any, Any, None]],
38
+ ) -> None: ...
39
+ async def asgi_http(
40
+ self,
41
+ scope: dict[str, Any],
42
+ receive: Callable[[], Coroutine[Any, Any, dict[str, Any]]],
43
+ send: Callable[[dict[str, Any]], Coroutine[Any, Any, None]],
44
+ ) -> None: ...
45
+ async def _build_start_response(
46
+ self,
47
+ ctx: dict[str, Any],
48
+ send: Callable[[dict[str, Any]], Coroutine[Any, Any, None]],
49
+ ) -> Callable[[str, list[tuple[str, str]]], None]: ...
50
+ async def _build_sender(
51
+ self,
52
+ ctx: dict[str, Any],
53
+ send: Callable[[dict[str, Any]], Coroutine[Any, Any, None]],
54
+ start_response: Callable[[str, list[tuple[str, str]]], None],
55
+ ) -> Callable[[Any], Coroutine[Any, Any, None]]: ...
56
+ async def _build_body(
57
+ self,
58
+ receive: Callable[[], Coroutine[Any, Any, dict[str, Any]]],
59
+ max_size: int = ...,
60
+ ) -> Any: ...
61
+ async def _build_environ(
62
+ self,
63
+ scope: dict[str, Any],
64
+ body: Any,
65
+ sender: Callable[[Any], Coroutine[Any, Any, None]],
66
+ ) -> dict[str, Any]: ...
67
+ def _ensure_start(
68
+ self,
69
+ ctx: dict[str, Any],
70
+ start_response: Callable[[str, list[tuple[str, str]]], None],
71
+ ) -> None: ...
72
+
73
+ def build_asgi(app_cls: type[ASGIApp]) -> Callable: ...
74
+ def build_asgi_i(app: ASGIApp) -> Callable: ...
appier/bus.pyi CHANGED
@@ -1,8 +1,54 @@
1
1
  from threading import Thread
2
+ from typing import Any, Callable, Self, Sequence
2
3
 
4
+ from .base import App
3
5
  from .component import Component
4
6
 
5
- class Bus(Component): ...
6
- class MemoryBus(Bus): ...
7
- class RedisBus(Bus): ...
8
- class RedisListener(Thread): ...
7
+ class Bus(Component):
8
+ def __init__(self, name: str = ..., owner: App = ..., *args, **kwargs): ...
9
+ @classmethod
10
+ def new(cls, *args, **kwargs) -> Self: ...
11
+ def bind(self, name: str, method: Callable) -> None: ...
12
+ def unbind(self, name: str, method: Callable | None = ...) -> None: ...
13
+ def trigger(self, name: str, *args, **kwargs) -> None: ...
14
+
15
+ class MemoryBus(Bus):
16
+ _events: dict[str, list[Callable]]
17
+ def __init__(self, name: str = ..., owner: App = ..., *args, **kwargs): ...
18
+ def bind(self, name: str, method: Callable) -> None: ...
19
+ def unbind(self, name: str, method: Callable | None = ...) -> None: ...
20
+ def trigger(self, name: str, *args, **kwargs) -> None: ...
21
+ def _load(self, *args, **kwargs) -> None: ...
22
+ def _unload(self, *args, **kwargs) -> None: ...
23
+ def _get_state(self) -> dict[str, Sequence[Callable]]: ...
24
+ def _set_state(self, state: dict[str, Sequence[Callable]]) -> None: ...
25
+
26
+ class RedisBus(Bus):
27
+ SERIALIZER: Any
28
+ GLOBAL_CHANNEL: str
29
+ _delay: bool
30
+ _events: dict[str, list[Callable]]
31
+ _name: str
32
+ _serializer: Any
33
+ _global_channel: str
34
+ _redis: Any
35
+ _pubsub: Any
36
+ _listener: RedisListener
37
+ def __init__(self, name: str = ..., owner: App = ..., *args, **kwargs): ...
38
+ def bind(self, name: str, method: Callable) -> None: ...
39
+ def unbind(self, name: str, method: Callable | None = ...) -> None: ...
40
+ def trigger(self, name: str, *args, **kwargs) -> None: ...
41
+ def _load(self, *args, **kwargs) -> None: ...
42
+ def _unload(self, *args, **kwargs) -> None: ...
43
+ def _get_state(self) -> dict[str, Sequence[Callable]]: ...
44
+ def _set_state(self, state: dict[str, Sequence[Callable]]) -> None: ...
45
+ def _open(self) -> None: ...
46
+ def _close(self) -> None: ...
47
+ def _loop(self, safe: bool = ...) -> None: ...
48
+ def _tick(self, item: dict[str, Any], safe: bool = ...) -> None: ...
49
+ def _to_channel(self, name: str) -> str: ...
50
+
51
+ class RedisListener(Thread):
52
+ _bus: RedisBus
53
+ def __init__(self, bus: RedisBus): ...
54
+ def run(self) -> None: ...
appier/config.py CHANGED
@@ -39,6 +39,7 @@ __license__ = "Apache License, Version 2.0"
39
39
  import os
40
40
  import sys
41
41
  import json
42
+ import contextlib
42
43
 
43
44
  from . import legacy
44
45
 
@@ -167,6 +168,31 @@ def conf_ctx():
167
168
  return dict(configs=dict(), config_f=dict())
168
169
 
169
170
 
171
+ @contextlib.contextmanager
172
+ def conf_override(name, value):
173
+ """
174
+ Context manager to temporarily override a configuration value.
175
+
176
+ Saves the original value, sets the new value, and restores the original
177
+ value (or removes it if it was None) when the context exits.
178
+
179
+ :type name: String
180
+ :param name: The name of the configuration key to override.
181
+ :type value: String
182
+ :param value: The temporary value to set for the configuration key.
183
+ """
184
+
185
+ original = conf(name, None)
186
+ conf_s(name, value)
187
+ try:
188
+ yield
189
+ finally:
190
+ if original == None:
191
+ conf_r(name)
192
+ else:
193
+ conf_s(name, original)
194
+
195
+
170
196
  def load(names=(FILE_NAME,), path=None, encoding="utf-8", ctx=None):
171
197
  paths = []
172
198
  homes = get_homes()
appier/geo.py CHANGED
@@ -38,6 +38,15 @@ from . import legacy
38
38
 
39
39
 
40
40
  class GeoResolver(object):
41
+ """
42
+ Resolves geo-location information for IP addresses using
43
+ a MaxMind city database that is loaded from disk.
44
+
45
+ The resolver tries known file locations, downloads the
46
+ compressed database when missing and exposes a simplified
47
+ structure for easier consumption.
48
+ """
49
+
41
50
  DB_NAME = "GeoLite2-City.mmdb"
42
51
  """ The name of the file that contains the GeoIP
43
52
  information database (to be used in execution) """
appier/graph.py CHANGED
@@ -105,7 +105,7 @@ class Graph(object):
105
105
  queue.push(src, priority=0)
106
106
 
107
107
  while queue.length() > 0:
108
- (_, _, top) = queue.pop(full=True)
108
+ _, _, top = queue.pop(full=True)
109
109
  dist[top] = dist[top] if top in dist else defines.INFINITY
110
110
 
111
111
  edges = self.edges[top] if top in self.edges else []
appier/model.pyi CHANGED
@@ -42,6 +42,18 @@ class Model:
42
42
  before_callbacks: Sequence[Callable[[Self], None]] = ...,
43
43
  after_callbacks: Sequence[Callable[[Self], None]] = ...,
44
44
  ) -> Self: ...
45
+ def pre_validate(self) -> None: ...
46
+ def pre_save(self) -> None: ...
47
+ def pre_create(self) -> None: ...
48
+ def pre_update(self) -> None: ...
49
+ def pre_delete(self) -> None: ...
50
+ def post_validate(self) -> None: ...
51
+ def post_save(self) -> None: ...
52
+ def post_create(self) -> None: ...
53
+ def post_update(self) -> None: ...
54
+ def post_delete(self) -> None: ...
55
+ def pre_apply(self) -> None: ...
56
+ def post_apply(self) -> None: ...
45
57
  @property
46
58
  def identity(self) -> Any: ...
47
59
  ...
appier/storage.py CHANGED
@@ -53,19 +53,19 @@ class StorageEngine(object):
53
53
  raise exceptions.NotImplementedError()
54
54
 
55
55
  @classmethod
56
- def seek(self, file, *args, **kwargs):
56
+ def seek(cls, file, *args, **kwargs):
57
57
  raise exceptions.NotImplementedError()
58
58
 
59
59
  @classmethod
60
- def cleanup(self, file, *args, **kwargs):
60
+ def cleanup(cls, file, *args, **kwargs):
61
61
  raise exceptions.NotImplementedError()
62
62
 
63
63
  @classmethod
64
- def is_seekable(self):
64
+ def is_seekable(cls):
65
65
  return False
66
66
 
67
67
  @classmethod
68
- def is_stored(self):
68
+ def is_stored(cls):
69
69
  return False
70
70
 
71
71
  @classmethod
@@ -75,7 +75,9 @@ class StorageEngine(object):
75
75
 
76
76
  class BaseEngine(StorageEngine):
77
77
  @classmethod
78
- def load(cls, file, *args, **kwargs):
78
+ def load( # pyright: ignore[reportIncompatibleMethodOverride]
79
+ cls, file, *args, **kwargs
80
+ ):
79
81
  force = kwargs.get("force", False)
80
82
  if not file.file_name:
81
83
  return
@@ -92,18 +94,24 @@ class BaseEngine(StorageEngine):
92
94
  finally:
93
95
  handle.close()
94
96
 
95
- cls._compute()
97
+ cls._compute(file)
96
98
 
97
99
  @classmethod
98
- def store(cls, file, *args, **kwargs):
100
+ def store( # pyright: ignore[reportIncompatibleMethodOverride]
101
+ cls, file, *args, **kwargs
102
+ ):
99
103
  pass
100
104
 
101
105
  @classmethod
102
- def delete(cls, file, *args, **kwargs):
106
+ def delete( # pyright: ignore[reportIncompatibleMethodOverride]
107
+ cls, file, *args, **kwargs
108
+ ):
103
109
  pass
104
110
 
105
111
  @classmethod
106
- def read(cls, file, *args, **kwargs):
112
+ def read( # pyright: ignore[reportIncompatibleMethodOverride]
113
+ cls, file, *args, **kwargs
114
+ ):
107
115
  # tries to determine the requested size for# the file
108
116
  # reading in case none is defined the handled flag
109
117
  # handling is ignored and the data returned immediately
@@ -125,19 +133,23 @@ class BaseEngine(StorageEngine):
125
133
  return None
126
134
 
127
135
  @classmethod
128
- def cleanup(cls, file, *args, **kwargs):
136
+ def cleanup( # pyright: ignore[reportIncompatibleMethodOverride]
137
+ cls, file, *args, **kwargs
138
+ ):
129
139
  if not hasattr(file, "handled"):
130
140
  return
131
141
  del file._handled
132
142
 
133
143
  @classmethod
134
- def is_stored(self):
144
+ def is_stored(cls): # pyright: ignore[reportIncompatibleMethodOverride]
135
145
  return True
136
146
 
137
147
 
138
148
  class FsEngine(StorageEngine):
139
149
  @classmethod
140
- def store(cls, file, *args, **kwargs):
150
+ def store( # pyright: ignore[reportIncompatibleMethodOverride]
151
+ cls, file, *args, **kwargs
152
+ ):
141
153
  file_path = cls._file_path(file)
142
154
  file_data = file.data or b""
143
155
  handle = open(file_path, "wb")
@@ -147,16 +159,22 @@ class FsEngine(StorageEngine):
147
159
  handle.close()
148
160
 
149
161
  @classmethod
150
- def load(cls, file, *args, **kwargs):
162
+ def load( # pyright: ignore[reportIncompatibleMethodOverride]
163
+ cls, file, *args, **kwargs
164
+ ):
151
165
  cls._compute(file)
152
166
 
153
167
  @classmethod
154
- def delete(cls, file, *args, **kwargs):
168
+ def delete( # pyright: ignore[reportIncompatibleMethodOverride]
169
+ cls, file, *args, **kwargs
170
+ ):
155
171
  file_path = cls._file_path(file, ensure=False)
156
172
  os.remove(file_path)
157
173
 
158
174
  @classmethod
159
- def read(cls, file, *args, **kwargs):
175
+ def read( # pyright: ignore[reportIncompatibleMethodOverride]
176
+ cls, file, *args, **kwargs
177
+ ):
160
178
  data = None
161
179
  size = kwargs.get("size", None)
162
180
  handle = cls._handle(file)
@@ -164,11 +182,14 @@ class FsEngine(StorageEngine):
164
182
  data = handle.read(size or -1)
165
183
  finally:
166
184
  is_final = True if not size or not data else False
167
- is_final and cls.cleanup(file)
185
+ if is_final:
186
+ cls.cleanup(file)
168
187
  return data
169
188
 
170
189
  @classmethod
171
- def seek(cls, file, *args, **kwargs):
190
+ def seek( # pyright: ignore[reportIncompatibleMethodOverride]
191
+ cls, file, *args, **kwargs
192
+ ):
172
193
  offset = kwargs.get("offset", None)
173
194
  if offset == None:
174
195
  return
@@ -176,14 +197,16 @@ class FsEngine(StorageEngine):
176
197
  handle.seek(offset)
177
198
 
178
199
  @classmethod
179
- def cleanup(cls, file, *args, **kwargs):
200
+ def cleanup( # pyright: ignore[reportIncompatibleMethodOverride]
201
+ cls, file, *args, **kwargs
202
+ ):
180
203
  if not hasattr(file, "_handle"):
181
204
  return
182
205
  file._handle.close()
183
206
  del file._handle
184
207
 
185
208
  @classmethod
186
- def is_seekable(self):
209
+ def is_seekable(cls): # pyright: ignore[reportIncompatibleMethodOverride]
187
210
  return True
188
211
 
189
212
  @classmethod
@@ -207,7 +230,7 @@ class FsEngine(StorageEngine):
207
230
  @classmethod
208
231
  def _file_path(cls, file, ensure=True, base=None):
209
232
  # verifies that the standard params value is defined and
210
- # if that's no the case defaults the value, then tries to
233
+ # if that's not the case defaults the value, then tries to
211
234
  # retrieve a series of parameters for file path discovery
212
235
  params = file.params or {}
213
236
  file_path = params.get("file_path", None)
appier/storage.pyi ADDED
@@ -0,0 +1,61 @@
1
+ from typing import Any
2
+
3
+ from .typesf import File
4
+
5
+ class StorageEngine:
6
+ @classmethod
7
+ def load(cls, file: File, *args, **kwargs) -> None: ...
8
+ @classmethod
9
+ def store(cls, file: File, *args, **kwargs) -> None: ...
10
+ @classmethod
11
+ def delete(cls, file: File, *args, **kwargs) -> None: ...
12
+ @classmethod
13
+ def read(cls, file: File, *args, **kwargs) -> bytes | None: ...
14
+ @classmethod
15
+ def seek(cls, file: File, *args, **kwargs) -> None: ...
16
+ @classmethod
17
+ def cleanup(cls, file: File, *args, **kwargs) -> None: ...
18
+ @classmethod
19
+ def is_seekable(cls) -> bool: ...
20
+ @classmethod
21
+ def is_stored(cls) -> bool: ...
22
+ @classmethod
23
+ def _compute(cls, file: File, *args, **kwargs) -> None: ...
24
+
25
+ class BaseEngine(StorageEngine):
26
+ @classmethod
27
+ def load(cls, file: File, *args, **kwargs) -> None: ...
28
+ @classmethod
29
+ def store(cls, file: File, *args, **kwargs) -> None: ...
30
+ @classmethod
31
+ def delete(cls, file: File, *args, **kwargs) -> None: ...
32
+ @classmethod
33
+ def read(cls, file: File, *args, **kwargs) -> bytes | None: ...
34
+ @classmethod
35
+ def cleanup(cls, file: File, *args, **kwargs) -> None: ...
36
+ @classmethod
37
+ def is_stored(cls) -> bool: ...
38
+
39
+ class FsEngine(StorageEngine):
40
+ @classmethod
41
+ def store(cls, file: File, *args, **kwargs) -> None: ...
42
+ @classmethod
43
+ def load(cls, file: File, *args, **kwargs) -> None: ...
44
+ @classmethod
45
+ def delete(cls, file: File, *args, **kwargs) -> None: ...
46
+ @classmethod
47
+ def read(cls, file: File, *args, **kwargs) -> bytes | None: ...
48
+ @classmethod
49
+ def seek(cls, file: File, *args, **kwargs) -> None: ...
50
+ @classmethod
51
+ def cleanup(cls, file: File, *args, **kwargs) -> None: ...
52
+ @classmethod
53
+ def is_seekable(cls) -> bool: ...
54
+ @classmethod
55
+ def _compute(cls, file: File) -> None: ...
56
+ @classmethod
57
+ def _handle(cls, file: File) -> Any: ...
58
+ @classmethod
59
+ def _file_path(
60
+ cls, file: File, ensure: bool = ..., base: str | None = ...
61
+ ) -> str: ...
appier/test/smtp.py CHANGED
@@ -43,14 +43,11 @@ class SMTPTest(unittest.TestCase):
43
43
  mime["To"] = ", ".join([address_mime])
44
44
 
45
45
  result = mime.as_string()
46
- self.assertEqual(
47
- result,
48
- 'Content-Type: text/plain; charset="utf-8"\n\
46
+ self.assertEqual(result, 'Content-Type: text/plain; charset="utf-8"\n\
49
47
  MIME-Version: 1.0\n\
50
48
  Content-Transfer-Encoding: base64\n\
51
49
  Subject: Hello World\n\
52
50
  From: =?utf-8?q?Jo=C3=A3o_Magalh=C3=A3es?= <joamag@hive.pt>\n\
53
51
  To: =?utf-8?q?Jo=C3=A3o_Magalh=C3=A3es?= <joamag@hive.pt>\n\
54
52
  \n\
55
- SGVsbG8gV29ybGQ=\n',
56
- )
53
+ SGVsbG8gV29ybGQ=\n')
appier/test/storage.py ADDED
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # Hive Appier Framework
5
+ # Copyright (c) 2008-2024 Hive Solutions Lda.
6
+ #
7
+ # This file is part of Hive Appier Framework.
8
+ #
9
+ # Hive Appier Framework is free software: you can redistribute it and/or modify
10
+ # it under the terms of the Apache License as published by the Apache
11
+ # Foundation, either version 2.0 of the License, or (at your option) any
12
+ # later version.
13
+ #
14
+ # Hive Appier Framework is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # Apache License for more details.
18
+ #
19
+ # You should have received a copy of the Apache License along with
20
+ # Hive Appier Framework. If not, see <http://www.apache.org/licenses/>.
21
+
22
+ __author__ = "João Magalhães <joamag@hive.pt>"
23
+ """ The author(s) of the module """
24
+
25
+ __copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda."
26
+ """ The copyright for the module """
27
+
28
+ __license__ = "Apache License, Version 2.0"
29
+ """ The license for the module """
30
+
31
+ import os
32
+ import shutil
33
+ import tempfile
34
+ import unittest
35
+
36
+ import appier
37
+
38
+
39
+ class StorageEngineTest(unittest.TestCase):
40
+ def test_is_seekable(self):
41
+ self.assertEqual(appier.StorageEngine.is_seekable(), False)
42
+
43
+ def test_is_stored(self):
44
+ self.assertEqual(appier.StorageEngine.is_stored(), False)
45
+
46
+
47
+ class BaseEngineTest(unittest.TestCase):
48
+ def test_is_stored(self):
49
+ self.assertEqual(appier.BaseEngine.is_stored(), True)
50
+
51
+
52
+ class FsEngineTest(unittest.TestCase):
53
+ def setUp(self):
54
+ self.temp_dir = tempfile.mkdtemp()
55
+
56
+ def tearDown(self):
57
+ if os.path.exists(self.temp_dir):
58
+ shutil.rmtree(self.temp_dir)
59
+
60
+ def test_store(self):
61
+ file_data = b"Hello World"
62
+ file = appier.File((b"test.txt", None, file_data))
63
+ file.engine = "fs"
64
+ file.params = {"file_path": os.path.join(self.temp_dir, "test.txt")}
65
+
66
+ appier.FsEngine.store(file)
67
+
68
+ file_path = os.path.join(self.temp_dir, "test.txt")
69
+ self.assertEqual(os.path.exists(file_path), True)
70
+
71
+ handle = open(file_path, "rb")
72
+ try:
73
+ data = handle.read()
74
+ finally:
75
+ handle.close()
76
+
77
+ self.assertEqual(data, file_data)
78
+
79
+ def test_load(self):
80
+ file_data = b"Hello World"
81
+ file_path = os.path.join(self.temp_dir, "test.txt")
82
+
83
+ handle = open(file_path, "wb")
84
+ try:
85
+ handle.write(file_data)
86
+ finally:
87
+ handle.close()
88
+
89
+ file = appier.File((b"test.txt", None, file_data))
90
+ file.engine = "fs"
91
+ file.params = {"file_path": file_path}
92
+
93
+ appier.FsEngine.load(file)
94
+
95
+ self.assertEqual(file.size, len(file_data))
96
+ self.assertEqual(file.hash != None, True)
97
+ self.assertEqual(file.etag != None, True)
98
+
99
+ def test_delete(self):
100
+ file_data = b"Hello World"
101
+ file_path = os.path.join(self.temp_dir, "test.txt")
102
+
103
+ handle = open(file_path, "wb")
104
+ try:
105
+ handle.write(file_data)
106
+ finally:
107
+ handle.close()
108
+
109
+ self.assertEqual(os.path.exists(file_path), True)
110
+
111
+ file = appier.File((b"test.txt", None, file_data))
112
+ file.engine = "fs"
113
+ file.params = {"file_path": file_path}
114
+
115
+ appier.FsEngine.delete(file)
116
+
117
+ self.assertEqual(os.path.exists(file_path), False)
118
+
119
+ def test_read(self):
120
+ file_data = b"Hello World"
121
+ file_path = os.path.join(self.temp_dir, "test.txt")
122
+
123
+ handle = open(file_path, "wb")
124
+ try:
125
+ handle.write(file_data)
126
+ finally:
127
+ handle.close()
128
+
129
+ file = appier.File((b"test.txt", None, file_data))
130
+ file.engine = "fs"
131
+ file.params = {"file_path": file_path}
132
+
133
+ data = appier.FsEngine.read(file)
134
+
135
+ self.assertEqual(data, file_data)
136
+
137
+ def test_read_size(self):
138
+ file_data = b"Hello World"
139
+ file_path = os.path.join(self.temp_dir, "test.txt")
140
+
141
+ handle = open(file_path, "wb")
142
+ try:
143
+ handle.write(file_data)
144
+ finally:
145
+ handle.close()
146
+
147
+ file = appier.File((b"test.txt", None, file_data))
148
+ file.engine = "fs"
149
+ file.params = {"file_path": file_path}
150
+
151
+ data = appier.FsEngine.read(file, size=5)
152
+
153
+ self.assertEqual(data, b"Hello")
154
+
155
+ data = appier.FsEngine.read(file, size=6)
156
+
157
+ self.assertEqual(data, b" World")
158
+
159
+ data = appier.FsEngine.read(file, size=6)
160
+
161
+ self.assertEqual(data, b"")
162
+
163
+ def test_seek(self):
164
+ file_data = b"Hello World"
165
+ file_path = os.path.join(self.temp_dir, "test.txt")
166
+
167
+ handle = open(file_path, "wb")
168
+ try:
169
+ handle.write(file_data)
170
+ finally:
171
+ handle.close()
172
+
173
+ file = appier.File((b"test.txt", None, file_data))
174
+ file.engine = "fs"
175
+ file.params = {"file_path": file_path}
176
+
177
+ appier.FsEngine.seek(file, offset=6)
178
+
179
+ data = appier.FsEngine.read(file)
180
+
181
+ self.assertEqual(data, b"World")
182
+
183
+ def test_cleanup(self):
184
+ file_data = b"Hello World"
185
+ file_path = os.path.join(self.temp_dir, "test.txt")
186
+
187
+ handle = open(file_path, "wb")
188
+ try:
189
+ handle.write(file_data)
190
+ finally:
191
+ handle.close()
192
+
193
+ file = appier.File((b"test.txt", None, file_data))
194
+ file.engine = "fs"
195
+ file.params = {"file_path": file_path}
196
+
197
+ appier.FsEngine._handle(file)
198
+
199
+ self.assertEqual(hasattr(file, "_handle"), True)
200
+
201
+ appier.FsEngine.cleanup(file)
202
+
203
+ self.assertEqual(hasattr(file, "_handle"), False)
204
+
205
+ def test_is_seekable(self):
206
+ self.assertEqual(appier.FsEngine.is_seekable(), True)
207
+
208
+ def test_file_path(self):
209
+ file = appier.File((b"test.txt", None, b"Hello World"))
210
+ file.engine = "fs"
211
+ file.params = {"file_path": os.path.join(self.temp_dir, "custom.txt")}
212
+
213
+ file_path = appier.FsEngine._file_path(file)
214
+
215
+ self.assertEqual(file_path, os.path.join(self.temp_dir, "custom.txt"))
216
+
217
+ def test_file_path_default(self):
218
+ file = appier.File((b"test.txt", None, b"Hello World"))
219
+ file.engine = "fs"
220
+
221
+ file_path = appier.FsEngine._file_path(file, base=self.temp_dir)
222
+
223
+ expected_path = os.path.join(self.temp_dir, file.guid)
224
+ self.assertEqual(file_path, expected_path)
225
+
226
+ def test_file_path_ensure(self):
227
+ sub_dir = os.path.join(self.temp_dir, "sub", "directory")
228
+ file = appier.File((b"test.txt", None, b"Hello World"))
229
+ file.engine = "fs"
230
+ file.params = {"file_path": os.path.join(sub_dir, "test.txt")}
231
+
232
+ self.assertEqual(os.path.exists(sub_dir), False)
233
+
234
+ file_path = appier.FsEngine._file_path(file, ensure=True)
235
+
236
+ self.assertEqual(os.path.exists(sub_dir), True)
237
+ self.assertEqual(file_path, os.path.join(sub_dir, "test.txt"))
238
+
239
+ def test_compute(self):
240
+ file_data = b"Hello World"
241
+ file_path = os.path.join(self.temp_dir, "test.txt")
242
+
243
+ handle = open(file_path, "wb")
244
+ try:
245
+ handle.write(file_data)
246
+ finally:
247
+ handle.close()
248
+
249
+ file = appier.File((b"test.txt", None, file_data))
250
+ file.engine = "fs"
251
+ file.params = {"file_path": file_path}
252
+
253
+ appier.FsEngine._compute(file)
254
+
255
+ self.assertEqual(file.size, len(file_data))
256
+ self.assertEqual(type(file.hash), str)
257
+ self.assertEqual(type(file.etag), str)
258
+ self.assertEqual(file.hash, file.etag)
259
+
260
+ def test_handle(self):
261
+ file_data = b"Hello World"
262
+ file_path = os.path.join(self.temp_dir, "test.txt")
263
+
264
+ handle = open(file_path, "wb")
265
+ try:
266
+ handle.write(file_data)
267
+ finally:
268
+ handle.close()
269
+
270
+ file = appier.File((b"test.txt", None, file_data))
271
+ file.engine = "fs"
272
+ file.params = {"file_path": file_path}
273
+
274
+ handle = appier.FsEngine._handle(file)
275
+
276
+ self.assertEqual(hasattr(file, "_handle"), True)
277
+ self.assertEqual(handle != None, True)
278
+ self.assertEqual(handle.closed, False)
279
+
280
+ appier.FsEngine.cleanup(file)
281
+
282
+ def test_integration(self):
283
+ file_data = b"Hello World"
284
+ file = appier.File((b"test.txt", None, file_data))
285
+ file.engine = "fs"
286
+ file.params = {"file_path": os.path.join(self.temp_dir, "test.txt")}
287
+
288
+ appier.FsEngine.store(file)
289
+
290
+ file2 = appier.File((b"test.txt", None, file_data))
291
+ file2.engine = "fs"
292
+ file2.params = {"file_path": os.path.join(self.temp_dir, "test.txt")}
293
+
294
+ appier.FsEngine.load(file2)
295
+
296
+ self.assertEqual(file2.size, len(file_data))
297
+
298
+ data = appier.FsEngine.read(file2)
299
+
300
+ self.assertEqual(data, file_data)
301
+
302
+ appier.FsEngine.delete(file2)
303
+
304
+ file_path = os.path.join(self.temp_dir, "test.txt")
305
+ self.assertEqual(os.path.exists(file_path), False)
appier/typesf.pyi ADDED
@@ -0,0 +1,104 @@
1
+ from typing import Any, Iterator, Sequence
2
+
3
+ FileLike = File | bytes | dict | tuple
4
+
5
+ class AbstractType:
6
+ def json_v(self, *args, **kwargs) -> Any: ...
7
+ def map_v(self, *args, **kwargs) -> Any: ...
8
+
9
+ class Type(AbstractType):
10
+ def __init__(self, value: Any): ...
11
+ def json_v(self, *args, **kwargs) -> Any: ...
12
+ def loads(self, value: Any) -> None: ...
13
+ def dumps(self) -> Any: ...
14
+
15
+ class File(AbstractType):
16
+ data: bytes | None
17
+ data_b64: str | None
18
+ file: Any | None
19
+ hash: str | None
20
+ size: int
21
+ file_name: str
22
+ mime: str | None
23
+ etag: str | None
24
+ guid: str
25
+ params: dict[str, Any] | None
26
+ engine: str | None
27
+ def __init__(self, file: FileLike): ...
28
+ def __len__(self) -> int: ...
29
+ @classmethod
30
+ def open(cls, path: str) -> File: ...
31
+ def build_d(self, file_d: bytes, name: str = ...) -> None: ...
32
+ def build_b64(self, file_m: dict[str, Any]) -> None: ...
33
+ def build_t(self, file_t: tuple[str, str | None, bytes | None]) -> None: ...
34
+ def build_i(self, file: File) -> None: ...
35
+ def build_f(self, file: Any) -> None: ...
36
+ def read(self, size: int | None = ...) -> bytes | None: ...
37
+ def seek(self, offset: int | None = ...) -> None: ...
38
+ def delete(self) -> None: ...
39
+ def cleanup(self) -> None: ...
40
+ def save(self, path: str | None = ...) -> None: ...
41
+ def json_v(self, *args, **kwargs) -> dict[str, Any] | None: ...
42
+ def is_seekable(self) -> bool: ...
43
+ def is_stored(self) -> bool: ...
44
+ def is_valid(self) -> bool: ...
45
+ def is_empty(self) -> bool: ...
46
+ def _hash(self, data: bytes | None) -> str | None: ...
47
+ def _etag(self, data: bytes | None) -> str | None: ...
48
+ def _guid(self) -> str: ...
49
+ def _load(self, force: bool = ...) -> None: ...
50
+ def _store(self, force: bool = ...) -> None: ...
51
+ def _compute(self) -> None: ...
52
+ def _engine(self) -> Any: ...
53
+
54
+ class Files(AbstractType):
55
+ _files: list[File]
56
+ def __init__(self, files: Files | Sequence[FileLike]): ...
57
+ def __len__(self) -> int: ...
58
+ def __iter__(self) -> Iterator[File]: ...
59
+ def __getitem__(self, key: int) -> File: ...
60
+ def base(self) -> type[File]: ...
61
+ def build_i(self, files: Files) -> None: ...
62
+ def build_f(self, files: Any) -> None: ...
63
+ def json_v(self, *args, **kwargs) -> list[dict[str, Any]]: ...
64
+ def is_empty(self) -> bool: ...
65
+ def _load(self) -> None: ...
66
+
67
+ class ImageFile(File):
68
+ width: int
69
+ height: int
70
+ format: str | None
71
+ kwargs: dict[str, Any]
72
+ def build_b64(self, file_m: dict[str, Any]) -> None: ...
73
+ def build_t(self, file_t: tuple[str, str | None, bytes | None]) -> None: ...
74
+ def build_i(self, file: File) -> None: ...
75
+ def build_f(self, file: Any) -> None: ...
76
+ def json_v(self, *args, **kwargs) -> dict[str, Any] | None: ...
77
+ def _ensure_all(self) -> None: ...
78
+ def _ensure_size(self) -> None: ...
79
+ def _ensure_mime(self) -> None: ...
80
+ def _ensure_kwargs(self) -> None: ...
81
+ def _size(self) -> tuple[int, int]: ...
82
+ def _size_image(self) -> tuple[int, int]: ...
83
+ def _size_pil(self) -> tuple[int, int]: ...
84
+ def _size_default(self) -> tuple[int, int]: ...
85
+ def _mime(self) -> tuple[str | None, str]: ...
86
+ def _mime_image(self) -> tuple[str | None, str]: ...
87
+ def _mime_pil(self) -> tuple[str | None, str]: ...
88
+ def _mime_default(self) -> tuple[str | None, str]: ...
89
+
90
+ class ImageFiles(Files):
91
+ def base(self) -> type[ImageFile]: ...
92
+
93
+ def image(
94
+ width: int | None = ..., height: int | None = ..., format: str = ..., **kwargs
95
+ ) -> type[ImageFile]: ...
96
+ def images(
97
+ width: int | None = ..., height: int | None = ..., format: str = ..., **kwargs
98
+ ) -> type[ImageFiles]: ...
99
+
100
+ class Reference(AbstractType): ...
101
+
102
+ def reference(
103
+ target: Any, name: str | None = ..., dumpall: bool = ...
104
+ ) -> type[Reference]: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: appier
3
- Version: 1.34.12
3
+ Version: 1.35.1
4
4
  Summary: Appier Framework
5
5
  Home-page: http://appier.hive.pt
6
6
  Author: Hive Solutions Lda.
@@ -1,7 +1,10 @@
1
- appier/__init__.py,sha256=YsoKeAEMyD5OWJWGe0x6fufIypPkKj4W5B0I31jqfjE,9449
1
+ appier/__init__.py,sha256=3ruSiBwKkttroZWrtHHvMxxutjPJOvC9F-1qC2cfqvM,9510
2
2
  appier/amqp.py,sha256=etYxUlfaK27Og_9FJ6qCgNLSYhnz9XgVhIhSmD2ITW4,3852
3
+ appier/amqp.pyi,sha256=vTsGOj0IjsRiRxdKfIAg4hTu2kHwPosZT24HlEIZfQ8,377
3
4
  appier/api.py,sha256=ZWRdjrEEUvCDcW_pp2BDgGCdNnS7tjdaZSzUfzSRzmQ,14778
4
- appier/asgi.py,sha256=Gubv7W8aLeLhaBxInE2Gc7Y2rxacbSlglAaKj25T6NY,12622
5
+ appier/api.pyi,sha256=iStUeSkn1zwt8OHfVEeasWRmp_OjB897LuXRI_abR4g,4934
6
+ appier/asgi.py,sha256=cjC3OUJ4XB3LzTdoJcM6aHvi-VpPQ2kjLwJOYL9YgMA,12959
7
+ appier/asgi.pyi,sha256=PAbTl3yez12xz9p7VWzkjAn3doq6-DOZaQ9hdB6F32U,2641
5
8
  appier/async_neo.py,sha256=CqeVtKczXt3SZPvi0BYLujVTeN4i-sT8P1_0JPR5aiI,5448
6
9
  appier/async_old.py,sha256=83YuTmoZkLIeyNHLZusCXqxB_6onRmYdKJ-Sqd1Yi1E,9195
7
10
  appier/asynchronous.py,sha256=a1LQa3wbGMaXELhF7W71dRr1klPOj6x-ST9EInvPhtU,1757
@@ -9,14 +12,14 @@ appier/asynchronous.pyi,sha256=5CpLkpKcUq09woMOVYwpl24Pli0A8Xu6nXoT20xDQ-o,104
9
12
  appier/base.py,sha256=6OhCkyM8MNa-eOIXXaYOCui0xUbSPq07ZJGqcXiwVJ0,272193
10
13
  appier/base.pyi,sha256=CwhdTt6lVGiXsXQewN1kkISD1YRNPCUf9U8fxYUVI3E,2924
11
14
  appier/bus.py,sha256=GyzP_qkTRDNi-NA9XV0U3YnibUn2TWH0w2fGG_a0IWo,8086
12
- appier/bus.pyi,sha256=W6_MIBpvDq468wfT5XM2WpPuR3O49QiZMOSCdMsJWos,182
15
+ appier/bus.pyi,sha256=jkTscpMMhiw4CrnUAhiGqNcxqengfaNFvdLNuOA_1i4,2267
13
16
  appier/cache.py,sha256=V1nhf4aHJpUlcFtxVsd-2tGCt1BarkxvHWwVwuhc4Ao,10689
14
17
  appier/cache.pyi,sha256=nrLCe6nAu_wLCa2juaALjPCQsNzIffrY_rb5MlGMrX0,176
15
18
  appier/common.py,sha256=fcECBvu-KcB9DImZzkpM_FA_PUqgCx0FKP4KHuOikYc,1313
16
19
  appier/component.py,sha256=I6xDQp0dmYOarOgbyrXIfXa1RAsVHttd8JjUub15U2M,2988
17
20
  appier/component.pyi,sha256=zKO71mWgi7I8QapKajZHoWznrLyofHigCT4gJvDY3G8,22
18
21
  appier/compress.py,sha256=o3E-at5DjpW2s3uOljEqqfLLyPzEU9cSXHFISk0sPvA,4699
19
- appier/config.py,sha256=pV6RXtu_f6U4449CyxL216fv4GjCjHUAOivqHAMaFfg,11405
22
+ appier/config.py,sha256=6BkQHbj9kdRX-e3tJMuk1nXB4G-m5nANRmn3yFUSaaY,12089
20
23
  appier/controller.py,sha256=uKzPJnz6aqibspPKf4tfJRvVMtxdDv0RFb0ivcDoepU,2285
21
24
  appier/crypt.py,sha256=Kr7Rbil8_bUp-oE_c_Wu2LRgk8nmVb9ukCHSuXE7RSc,5874
22
25
  appier/data.py,sha256=inrM-kXXsx0jLqyetXMWdBuobZbAWBH1fnShV4NcW-Y,14764
@@ -28,16 +31,16 @@ appier/export.py,sha256=Sp1Z0wA7fxcJ2w2reNny67FN5Z44lCeEarQU_H8Nxcc,12105
28
31
  appier/extra.py,sha256=AwcpSg4qe7eVtNpaf5jy1hap8RcaoftGoNpGsFdvH90,1208
29
32
  appier/extra_neo.py,sha256=oI8qsjhA7tLieR1fSl67e2lAxDXkaD3xT4nkjvVFDz0,2342
30
33
  appier/extra_old.py,sha256=5mwfHIrz6Um7-QpXiqnEm_lXXpWhNvnMgPWIFVegItU,1991
31
- appier/geo.py,sha256=EEPh8oX7Uk7iert-XKt7HCw0swicc8lFAv7XFeQSbZ8,4877
34
+ appier/geo.py,sha256=5XzHFCwfUBNFe7rkvPKWV9LWswcbfMn9tPS1q_Twk0s,5178
32
35
  appier/git.py,sha256=-neM2mxqlyiCNu17CpLyiz3RN7Ti6LtAx5L1tipxAGA,9785
33
- appier/graph.py,sha256=jSgu-ookILujLm4ivW14SymVmuI1nbjbVZMgRS2ehOg,4054
36
+ appier/graph.py,sha256=9NdjTmlkUf6HbigBNB5ZvVKY2X7kafHhB8EXq7Xx-dM,4052
34
37
  appier/http.py,sha256=Ed-NL-VUGBCZtzIWMNZI0Z1iJ-lMbjLkF0WeH7nICdI,37662
35
38
  appier/legacy.py,sha256=o_oJ_2lqZELZKwkvfGt0aDam6ZSZiZiL7FYygNjrDbY,15881
36
39
  appier/log.py,sha256=jhV7ub5nZwrLzY7x-tZDJfb8tcsXu-ndWxxrCspBUdU,12825
37
40
  appier/meta.py,sha256=rgBLOjD6QU9CGYsbCQS3Fy4iY14uk1-Kd8ljkfmxxzc,7168
38
41
  appier/mock.py,sha256=WoWa67rb8qV_ogToQJCdT0R-rCw9RUY24EkA4bYR1G4,5800
39
42
  appier/model.py,sha256=0V_91vft3fMAFm5KzFILb-gdpoDqX6g66UGXxMYf_Jo,123021
40
- appier/model.pyi,sha256=prhI4tdSILryvLbDC65s4LwAt-LbkBtaTN5ltsotlOw,1897
43
+ appier/model.pyi,sha256=m6qpkKge8MfyZLHHng0oM0VszO1XJTdy9_UgSpBfSAE,2369
41
44
  appier/model_a.py,sha256=c6XpG4oIelXNDK0uicsZ69-f6isUgmh5-29F61PZt9c,16176
42
45
  appier/mongo.py,sha256=rU3lRroXij6x17aKFBAbuiu8Cu2xOIP-DbDtI59Ln44,11535
43
46
  appier/observer.py,sha256=T0QpkxdkcNuEOZJdmJT5nFSJ2e-0VPbGveX5YKtt7mA,4519
@@ -55,9 +58,11 @@ appier/session.py,sha256=6rcwFdsu--7gFLr6mJJh3EFSZ_A3Oe8u7olAvTw2sUk,20380
55
58
  appier/session.pyi,sha256=JBYcT1wZ9mKBBk9iTw2GDJENB8PWFzxXjIr9oXKe85Q,239
56
59
  appier/settings.py,sha256=28tTRJIZ47iwXqUs6YtUoW7ntEVsp_0-QfONVDGA1sg,1136
57
60
  appier/smtp.py,sha256=5tW0dOEm03kejLoQyhRQFjgjYRd-jhb-VlhEKDb4Zn0,3434
58
- appier/storage.py,sha256=Qp_CMCDg85q35n9lggNVBZRWwlGrQ-nLYNj0W7Mnbec,7470
61
+ appier/storage.py,sha256=qL8L_Db7vNMhK62Yd8r2qsLfnJ1eBKlbR8rfCSdEBsk,8351
62
+ appier/storage.pyi,sha256=Q1-vbdwjgEnIhuDXS5lcpaLGNk7SL7opXq0-xBeCVA8,2034
59
63
  appier/structures.py,sha256=lwAEiz4k1O4bywGcfHvOvZJusN-lUwcviihR9aZLMtY,8883
60
64
  appier/typesf.py,sha256=XIYRQ2M7ErjvjB0SAu4Rkv3aYUOmWhqfZy3sUYyHtpM,37451
65
+ appier/typesf.pyi,sha256=Y2cdZ2m2VKWam072hYQ12K_Uax2NKRCpoDkFXOBKuUI,3836
61
66
  appier/util.py,sha256=ogWRbNbyWcfVCU_pPzADXsMTwLi3Hd2wPOWzDw3K-IQ,84705
62
67
  appier/validation.py,sha256=dMxAELEKGo93ta2UpY88UQAbiKq61b9pHd5L5pAkECk,22800
63
68
  appier/res/static/css/base.css,sha256=J9zLozd57KoslAsrsj2a42glGTObxbkrrckR-W-_f2A,6127
@@ -89,14 +94,15 @@ appier/test/request.py,sha256=h3DdvhEMARpYTp7eRcJ3-qLQyG-a8PHqSnWY52BiOeU,8176
89
94
  appier/test/scheduler.py,sha256=j-0byKZZfD36EenJg8kVm55plZ5p1lTLz9GsOT0FDxE,8384
90
95
  appier/test/serialize.py,sha256=roX01n86AQfnPxzPVdIjdIUQWC_x0C_HE3hNgF37ci8,2120
91
96
  appier/test/session.py,sha256=KdiYLLB5autIEu1sHwOuYJXVd0y6RMPgg0ITBuRTMfA,4419
92
- appier/test/smtp.py,sha256=XJNa0ZTmabdrX8MfVMgcqBxabvBfHgIimvozdziP4_E,1826
97
+ appier/test/smtp.py,sha256=xFFbtkxrIu97ChbKA5jRC1wmei2o2GdyLpg3r_nsnHs,1788
98
+ appier/test/storage.py,sha256=GTrs-uo9-FyXbY_DC1gIKjRYi6y5ES0-q8XE1X13mUM,8932
93
99
  appier/test/structures.py,sha256=MRjUFRlnJi-i7YGWW5y792JbJwicNvOIzVAS220tgeQ,8367
94
100
  appier/test/tags.py,sha256=uf52pCsZL4-yp7i3Tk7F9T6aN4uYvYxbwcUUo-b7-0E,3861
95
101
  appier/test/typesf.py,sha256=KHumQFzx7wPZSCb8_mpIwobhIy2Fh_0XYviwPjXMbKI,9680
96
102
  appier/test/util.py,sha256=vfnleU3BUaqMs1mrpXjKom3V_zFOzrsjhmQ8sYp1GaQ,46668
97
103
  appier/test/validation.py,sha256=riOCsGKob1P5jnbcB5qGZ45ApimNAVS0byg9v_uUdrk,4952
98
- appier-1.34.12.dist-info/LICENSE,sha256=Pd-b5cKP4n2tFDpdx27qJSIq0d1ok0oEcGTlbtL6QMU,11560
99
- appier-1.34.12.dist-info/METADATA,sha256=rT3CvFXm-wQtf8GOQ3ePG0I-Jmuo0v9ngNkvlUfkfn8,1921
100
- appier-1.34.12.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110
101
- appier-1.34.12.dist-info/top_level.txt,sha256=Z2e_Y1ya06a554WwQZkfNRiaaQxqsdaPtBzrck384Lo,7
102
- appier-1.34.12.dist-info/RECORD,,
104
+ appier-1.35.1.dist-info/LICENSE,sha256=Pd-b5cKP4n2tFDpdx27qJSIq0d1ok0oEcGTlbtL6QMU,11560
105
+ appier-1.35.1.dist-info/METADATA,sha256=Cd_g6pvs3QMSs3j2H35WjGeLwpc-m8F09jGh_C4Awkc,1920
106
+ appier-1.35.1.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110
107
+ appier-1.35.1.dist-info/top_level.txt,sha256=Z2e_Y1ya06a554WwQZkfNRiaaQxqsdaPtBzrck384Lo,7
108
+ appier-1.35.1.dist-info/RECORD,,