dara-core 1.20.0a1__py3-none-any.whl → 1.20.1a1__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.
Files changed (82) hide show
  1. dara/core/__init__.py +0 -3
  2. dara/core/actions.py +2 -1
  3. dara/core/auth/basic.py +16 -22
  4. dara/core/auth/definitions.py +2 -2
  5. dara/core/auth/routes.py +5 -5
  6. dara/core/auth/utils.py +5 -5
  7. dara/core/base_definitions.py +64 -22
  8. dara/core/cli.py +7 -8
  9. dara/core/configuration.py +2 -5
  10. dara/core/css.py +2 -1
  11. dara/core/data_utils.py +19 -18
  12. dara/core/defaults.py +7 -6
  13. dara/core/definitions.py +19 -50
  14. dara/core/http.py +3 -7
  15. dara/core/interactivity/__init__.py +0 -6
  16. dara/core/interactivity/actions.py +50 -52
  17. dara/core/interactivity/any_data_variable.py +134 -7
  18. dara/core/interactivity/any_variable.py +8 -5
  19. dara/core/interactivity/data_variable.py +266 -8
  20. dara/core/interactivity/derived_data_variable.py +290 -7
  21. dara/core/interactivity/derived_variable.py +174 -414
  22. dara/core/interactivity/filtering.py +27 -46
  23. dara/core/interactivity/loop_variable.py +2 -2
  24. dara/core/interactivity/non_data_variable.py +68 -5
  25. dara/core/interactivity/plain_variable.py +15 -89
  26. dara/core/interactivity/switch_variable.py +19 -19
  27. dara/core/interactivity/url_variable.py +90 -10
  28. dara/core/internal/cache_store/base_impl.py +1 -2
  29. dara/core/internal/cache_store/cache_store.py +25 -22
  30. dara/core/internal/cache_store/keep_all.py +1 -4
  31. dara/core/internal/cache_store/lru.py +1 -5
  32. dara/core/internal/cache_store/ttl.py +1 -4
  33. dara/core/internal/cgroup.py +1 -1
  34. dara/core/internal/dependency_resolution.py +66 -60
  35. dara/core/internal/devtools.py +5 -12
  36. dara/core/internal/download.py +4 -13
  37. dara/core/internal/encoder_registry.py +7 -7
  38. dara/core/internal/execute_action.py +13 -13
  39. dara/core/internal/hashing.py +3 -1
  40. dara/core/internal/import_discovery.py +4 -3
  41. dara/core/internal/normalization.py +18 -9
  42. dara/core/internal/pandas_utils.py +5 -107
  43. dara/core/internal/pool/definitions.py +1 -1
  44. dara/core/internal/pool/task_pool.py +16 -25
  45. dara/core/internal/pool/utils.py +18 -21
  46. dara/core/internal/pool/worker.py +2 -3
  47. dara/core/internal/port_utils.py +1 -1
  48. dara/core/internal/registries.py +6 -12
  49. dara/core/internal/registry.py +2 -4
  50. dara/core/internal/registry_lookup.py +5 -11
  51. dara/core/internal/routing.py +145 -109
  52. dara/core/internal/scheduler.py +8 -13
  53. dara/core/internal/settings.py +2 -2
  54. dara/core/internal/store.py +29 -2
  55. dara/core/internal/tasks.py +195 -379
  56. dara/core/internal/utils.py +13 -36
  57. dara/core/internal/websocket.py +20 -21
  58. dara/core/js_tooling/js_utils.py +26 -28
  59. dara/core/js_tooling/templates/vite.config.template.ts +3 -12
  60. dara/core/logging.py +12 -13
  61. dara/core/main.py +11 -14
  62. dara/core/metrics/cache.py +1 -1
  63. dara/core/metrics/utils.py +3 -3
  64. dara/core/persistence.py +5 -27
  65. dara/core/umd/dara.core.umd.js +55425 -59091
  66. dara/core/visual/components/__init__.py +2 -2
  67. dara/core/visual/components/fallback.py +4 -30
  68. dara/core/visual/components/for_cmp.py +1 -4
  69. dara/core/visual/css/__init__.py +31 -30
  70. dara/core/visual/dynamic_component.py +28 -31
  71. dara/core/visual/progress_updater.py +3 -4
  72. {dara_core-1.20.0a1.dist-info → dara_core-1.20.1a1.dist-info}/METADATA +11 -12
  73. dara_core-1.20.1a1.dist-info/RECORD +114 -0
  74. dara/core/interactivity/client_variable.py +0 -71
  75. dara/core/interactivity/server_variable.py +0 -325
  76. dara/core/interactivity/state_variable.py +0 -69
  77. dara/core/interactivity/tabular_variable.py +0 -94
  78. dara/core/internal/multi_resource_lock.py +0 -70
  79. dara_core-1.20.0a1.dist-info/RECORD +0 -119
  80. {dara_core-1.20.0a1.dist-info → dara_core-1.20.1a1.dist-info}/LICENSE +0 -0
  81. {dara_core-1.20.0a1.dist-info → dara_core-1.20.1a1.dist-info}/WHEEL +0 -0
  82. {dara_core-1.20.0a1.dist-info → dara_core-1.20.1a1.dist-info}/entry_points.txt +0 -0
@@ -20,7 +20,6 @@ from __future__ import annotations
20
20
  import asyncio
21
21
  import inspect
22
22
  import os
23
- from collections.abc import Awaitable, Coroutine, Sequence
24
23
  from functools import wraps
25
24
  from importlib import import_module
26
25
  from importlib.util import find_spec
@@ -28,21 +27,21 @@ from types import ModuleType
28
27
  from typing import (
29
28
  TYPE_CHECKING,
30
29
  Any,
30
+ Awaitable,
31
31
  Callable,
32
+ Coroutine,
32
33
  Dict,
33
34
  Literal,
34
35
  Optional,
36
+ Sequence,
35
37
  Tuple,
36
- Type,
37
- TypeVar,
38
38
  Union,
39
39
  )
40
40
 
41
41
  import anyio
42
42
  from anyio import from_thread
43
- from exceptiongroup import BaseExceptionGroup, ExceptionGroup
43
+ from exceptiongroup import ExceptionGroup
44
44
  from starlette.concurrency import run_in_threadpool
45
- from typing_extensions import ParamSpec
46
45
 
47
46
  from dara.core.auth.definitions import SESSION_ID, USER
48
47
  from dara.core.base_definitions import CacheType
@@ -80,7 +79,7 @@ def get_cache_scope(cache_type: Optional[CacheType]) -> CacheScope:
80
79
  return 'global'
81
80
 
82
81
 
83
- async def run_user_handler(handler: Callable, args: Union[Sequence, None] = None, kwargs: Union[dict, None] = None):
82
+ async def run_user_handler(handler: Callable, args: Sequence = [], kwargs: dict = {}):
84
83
  """
85
84
  Run a user-defined handler function. Runs sync functions in a threadpool.
86
85
  Handles SystemExits cleanly.
@@ -89,10 +88,6 @@ async def run_user_handler(handler: Callable, args: Union[Sequence, None] = None
89
88
  :param args: list of arguments to pass to the function
90
89
  :param kwargs: dict of kwargs to past to the function
91
90
  """
92
- if args is None:
93
- args = []
94
- if kwargs is None:
95
- kwargs = {}
96
91
  with handle_system_exit('User defined function quit unexpectedly'):
97
92
  if inspect.iscoroutinefunction(handler):
98
93
  return await handler(*args, **kwargs)
@@ -169,21 +164,17 @@ def enforce_sso(conf: ConfigurationBuilder):
169
164
  Raises if SSO is not used
170
165
  """
171
166
  try:
172
- from dara.enterprise import SSOAuthConfig # pyright: ignore[reportMissingImports]
167
+ from dara.enterprise import SSOAuthConfig
173
168
 
174
169
  if conf.auth_config is None or not isinstance(conf.auth_config, SSOAuthConfig):
175
170
  raise ValueError('Config does not have SSO auth enabled. Please update your application to configure SSO.')
176
- except ImportError as err:
171
+ except ImportError:
177
172
  raise ValueError(
178
173
  'SSO is not enabled. Please install the dara_enterprise package and configure SSO to use this feature.'
179
- ) from err
174
+ )
180
175
 
181
176
 
182
- P = ParamSpec('P')
183
- T = TypeVar('T')
184
-
185
-
186
- def async_dedupe(fn: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:
177
+ def async_dedupe(fn: Callable[..., Awaitable]):
187
178
  """
188
179
  Decorator to deduplicate concurrent calls to asynchronous functions based on their arguments.
189
180
 
@@ -201,7 +192,7 @@ def async_dedupe(fn: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:
201
192
  is_method = 'self' in inspect.signature(fn).parameters
202
193
 
203
194
  @wraps(fn)
204
- async def wrapped(*args: P.args, **kwargs: P.kwargs) -> T:
195
+ async def wrapped(*args, **kwargs):
205
196
  non_self_args = args[1:] if is_method else args
206
197
  key = (non_self_args, frozenset(kwargs.items()))
207
198
  lock = locks.get(key)
@@ -237,22 +228,8 @@ def resolve_exception_group(error: Any):
237
228
 
238
229
  :param error: The error to resolve
239
230
  """
240
- if isinstance(error, ExceptionGroup) and len(error.exceptions) == 1:
241
- return resolve_exception_group(error.exceptions[0])
231
+ if isinstance(error, ExceptionGroup):
232
+ if len(error.exceptions) == 1:
233
+ return resolve_exception_group(error.exceptions[0])
242
234
 
243
235
  return error
244
-
245
-
246
- def exception_group_contains(err_type: Type[BaseException], group: BaseExceptionGroup) -> bool:
247
- """
248
- Check if an ExceptionGroup contains an error of a given type, recursively
249
-
250
- :param err_type: The type of error to check for
251
- :param group: The ExceptionGroup to check
252
- """
253
- for exc in group.exceptions:
254
- if isinstance(exc, err_type):
255
- return True
256
- if isinstance(exc, BaseExceptionGroup):
257
- return exception_group_contains(err_type, exc)
258
- return False
@@ -204,27 +204,28 @@ class WebSocketHandler:
204
204
  message_id = message.channel
205
205
 
206
206
  # If the message has a channel ID, it's a response to a previous message
207
- if message_id and message_id in self.pending_responses:
208
- event, existing_messages = self.pending_responses[message_id]
207
+ if message_id:
208
+ if message_id in self.pending_responses:
209
+ event, existing_messages = self.pending_responses[message_id]
210
+
211
+ # If the response is chunked then collect the messages in pending responses
212
+ if message.chunk_count is not None:
213
+ if existing_messages is not None and isinstance(existing_messages, list):
214
+ existing_messages.append(message.message)
215
+ else:
216
+ existing_messages = [message.message]
217
+ self.pending_responses[message_id] = (
218
+ event,
219
+ existing_messages,
220
+ )
209
221
 
210
- # If the response is chunked then collect the messages in pending responses
211
- if message.chunk_count is not None:
212
- if existing_messages is not None and isinstance(existing_messages, list):
213
- existing_messages.append(message.message)
222
+ # If all chunks have been received, set the event to notify the waiting coroutine
223
+ if len(existing_messages) == message.chunk_count:
224
+ event.set()
214
225
  else:
215
- existing_messages = [message.message]
216
- self.pending_responses[message_id] = (
217
- event,
218
- existing_messages,
219
- )
220
-
221
- # If all chunks have been received, set the event to notify the waiting coroutine
222
- if len(existing_messages) == message.chunk_count:
226
+ # Store the response and set the event to notify the waiting coroutine
227
+ self.pending_responses[message_id] = (event, message.message)
223
228
  event.set()
224
- else:
225
- # Store the response and set the event to notify the waiting coroutine
226
- self.pending_responses[message_id] = (event, message.message)
227
- event.set()
228
229
 
229
230
  return None
230
231
 
@@ -474,7 +475,7 @@ async def ws_handler(websocket: WebSocket, token: Optional[str] = Query(default=
474
475
  if pending_tokens_registry.has(token):
475
476
  pending_tokens_registry.remove(token)
476
477
 
477
- user_identifier = token_content.identity_id
478
+ user_identifier = token_content.identity_id or token_content.identity_name
478
479
 
479
480
  # Add the new session ID to known sessions for that user
480
481
  if sessions_registry.has(user_identifier):
@@ -496,8 +497,6 @@ async def ws_handler(websocket: WebSocket, token: Optional[str] = Query(default=
496
497
  SESSION_ID.set(token_data.session_id)
497
498
  ID_TOKEN.set(token_data.id_token)
498
499
 
499
- WS_CHANNEL.set(channel)
500
-
501
500
  # Set initial Auth context vars for the WS connection
502
501
  update_context(token_content)
503
502
 
@@ -15,7 +15,6 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- import contextlib
19
18
  import importlib
20
19
  import json
21
20
  import os
@@ -237,8 +236,10 @@ class BuildCache(BaseModel):
237
236
 
238
237
  # Create a symlink from the custom js folder into the static files directory
239
238
  new_path = os.path.abspath(os.path.join(self.static_files_dir, self.build_config.js_config.local_entry))
240
- with contextlib.suppress(FileNotFoundError):
239
+ try:
241
240
  os.unlink(new_path)
241
+ except FileNotFoundError:
242
+ pass
242
243
  os.symlink(absolute_path, new_path)
243
244
 
244
245
  # Create a symlink for the node modules in the custom_js folder
@@ -246,8 +247,10 @@ class BuildCache(BaseModel):
246
247
  new_node_modules_path = os.path.abspath(
247
248
  os.path.join(os.getcwd(), self.build_config.js_config.local_entry, 'node_modules')
248
249
  )
249
- with contextlib.suppress(FileNotFoundError):
250
+ try:
250
251
  os.unlink(new_node_modules_path)
252
+ except FileNotFoundError:
253
+ pass
251
254
  os.symlink(node_modules_path, new_node_modules_path)
252
255
 
253
256
  def get_importers(self) -> Dict[str, str]:
@@ -271,7 +274,7 @@ class BuildCache(BaseModel):
271
274
  """
272
275
  py_modules = set()
273
276
 
274
- for module in self.package_map:
277
+ for module in self.package_map.keys():
275
278
  py_modules.add(module)
276
279
 
277
280
  if 'dara.core' in py_modules:
@@ -313,9 +316,8 @@ class BuildCache(BaseModel):
313
316
  # Append core deps required for building/dev mode
314
317
  pkg_json['dependencies'] = {
315
318
  **deps,
316
- '@vitejs/plugin-react': '4.6.0',
317
- 'vite': '7.0.4',
318
- 'vite-plugin-node-polyfills': '0.24.0',
319
+ '@vitejs/plugin-react': '2.1.0',
320
+ 'vite': '3.1.8',
319
321
  }
320
322
 
321
323
  return pkg_json
@@ -432,16 +434,13 @@ def _get_module_file(module: str) -> str:
432
434
  return cast(str, imported_module.__file__)
433
435
 
434
436
 
435
- def rebuild_js(build_cache: BuildCache, build_diff: Union[BuildCacheDiff, None] = None):
437
+ def rebuild_js(build_cache: BuildCache, build_diff: BuildCacheDiff = BuildCacheDiff.full_diff()):
436
438
  """
437
439
  Generic 'rebuild' function which bundles/prepares assets depending on the build mode chosen
438
440
 
439
441
  :param build_cache: current build configuration cache
440
442
  :param build_diff: the difference between the current build cache and the previous build cache
441
443
  """
442
- if build_diff is None:
443
- build_diff = BuildCacheDiff.full_diff()
444
-
445
444
  # If we are in docker mode, skip the JS build
446
445
  if os.environ.get('DARA_DOCKER_MODE', 'FALSE') == 'TRUE':
447
446
  dev_logger.debug('Docker mode, skipping JS build')
@@ -486,16 +485,17 @@ def bundle_js(build_cache: BuildCache, copy_js: bool = False):
486
485
  :param copy_js: whether to copy JS instead of symlinking it
487
486
  """
488
487
  # If custom JS is present, symlink it
489
- if build_cache.build_config.js_config is not None and os.path.isdir(build_cache.build_config.js_config.local_entry):
490
- if copy_js:
491
- # Just move the directory to output
492
- js_folder_name = os.path.basename(build_cache.build_config.js_config.local_entry)
493
- shutil.copytree(
494
- build_cache.build_config.js_config.local_entry,
495
- os.path.join(build_cache.static_files_dir, js_folder_name),
496
- )
497
- else:
498
- build_cache.symlink_js()
488
+ if build_cache.build_config.js_config is not None:
489
+ if os.path.isdir(build_cache.build_config.js_config.local_entry):
490
+ if copy_js:
491
+ # Just move the directory to output
492
+ js_folder_name = os.path.basename(build_cache.build_config.js_config.local_entry)
493
+ shutil.copytree(
494
+ build_cache.build_config.js_config.local_entry,
495
+ os.path.join(build_cache.static_files_dir, js_folder_name),
496
+ )
497
+ else:
498
+ build_cache.symlink_js()
499
499
 
500
500
  # Determine template paths
501
501
  entry_template = os.path.join(pathlib.Path(__file__).parent.absolute(), 'templates/_entry.template.tsx')
@@ -544,18 +544,16 @@ def bundle_js(build_cache: BuildCache, copy_js: bool = False):
544
544
 
545
545
  cwd = os.getcwd()
546
546
  os.chdir(build_cache.static_files_dir)
547
- dev_logger.info('Installing JS dependencies...')
548
- exit_code = os.system(f'{package_manager} install') # nosec B605 # package_manager is validated
547
+ exit_code = os.system(f'{package_manager} install') # nosec B605 # package_manager is validated
549
548
  if exit_code > 0:
550
549
  raise SystemError(
551
550
  "Failed to install the JS dependencies - there's likely a connection issue or a broken package"
552
551
  )
553
- dev_logger.info('JS dependencies installed successfully')
554
552
 
555
553
  # Load entry template as a string
556
- with open(entry_template, encoding='utf-8') as f:
554
+ with open(entry_template, 'r', encoding='utf-8') as f:
557
555
  entry_template_str = f.read()
558
- with open(vite_template, encoding='utf-8') as f:
556
+ with open(vite_template, 'r', encoding='utf-8') as f:
559
557
  vite_template_str = f.read()
560
558
 
561
559
  # Convert importers dict to a string for injection into the template
@@ -576,7 +574,7 @@ def bundle_js(build_cache: BuildCache, copy_js: bool = False):
576
574
  dev_logger.warning('App is in DEV mode, running `dara dev` CLI command alongside this process is required')
577
575
  else:
578
576
  # Run build pointed at the generated entry file
579
- exit_code = os.system(f'{package_manager} run build') # nosec B605 # package_manager is validated
577
+ exit_code = os.system(f'{package_manager} run build') # nosec B605 # package_manager is validated
580
578
  if exit_code > 0:
581
579
  raise SystemError('Failed to build the JS part of the project')
582
580
 
@@ -642,7 +640,7 @@ def build_autojs_template(html_template: str, build_cache: BuildCache, config: C
642
640
  """
643
641
  settings = get_settings()
644
642
  entry_template = os.path.join(pathlib.Path(__file__).parent.absolute(), 'templates/_entry_autojs.template.tsx')
645
- with open(entry_template, encoding='utf-8') as f:
643
+ with open(entry_template, 'r', encoding='utf-8') as f:
646
644
  entry_template_str = f.read()
647
645
 
648
646
  importers_dict = build_cache.get_importers()
@@ -1,27 +1,18 @@
1
1
  import react from '@vitejs/plugin-react';
2
2
  import { defineConfig } from 'vite';
3
- import { nodePolyfills } from 'vite-plugin-node-polyfills';
4
3
 
5
4
  export default defineConfig({
6
5
  base: '',
7
6
  plugins: [
8
7
  react({
9
8
  jsxRuntime: 'classic',
10
- }),
11
- // Some package we're pulling requires node polyfills for stream
12
- nodePolyfills({
13
- globals: {
14
- process: true,
15
- Buffer: true,
16
- global: true,
17
- },
18
- }),
9
+ })
19
10
  ],
20
11
  publicDir: false,
21
12
  build: {
22
13
  outDir: '$$output$$',
23
14
  assetsDir: '',
24
- manifest: 'manifest.json',
15
+ manifest: true,
25
16
  rollupOptions: {
26
17
  input: './_entry.tsx',
27
18
  },
@@ -44,5 +35,5 @@ export default defineConfig({
44
35
  },
45
36
  worker: {
46
37
  format: 'es',
47
- },
38
+ }
48
39
  });
dara/core/logging.py CHANGED
@@ -74,7 +74,7 @@ class Logger:
74
74
 
75
75
  self._logger.warning(payload, extra={'content': extra})
76
76
 
77
- def error(self, title: str, error: BaseException, extra: Optional[Dict[str, Any]] = None):
77
+ def error(self, title: str, error: Exception, extra: Optional[Dict[str, Any]] = None):
78
78
  """
79
79
  Log a message at the ERROR level
80
80
 
@@ -135,7 +135,6 @@ class LoggingMiddleware(BaseHTTPMiddleware):
135
135
  # This is required so that requesting the body content doesn't hang the request
136
136
  if request.headers.get('Content-Type') == 'application/json' and content_length < one_mb:
137
137
  old_recieve = request._receive
138
-
139
138
  # Add the debug logging into a new receive call that wraps the old one. This is required to make
140
139
  # streaming requests and responses work as streaming sends further messages to trigger
141
140
  # sending/receiving further data
@@ -171,14 +170,11 @@ class LoggingMiddleware(BaseHTTPMiddleware):
171
170
  return response
172
171
 
173
172
 
174
- def _print_stacktrace(err: Optional[BaseException] = None) -> str:
173
+ def _print_stacktrace():
175
174
  """
176
175
  Prints out the current stack trace whilst unwinding all the logging calls on the way so it just shows the relevant
177
176
  parts. Will also extract any exceptions and print them at the end.
178
177
  """
179
- if err is not None:
180
- return ''.join(traceback.format_exception(type(err), err, err.__traceback__))
181
-
182
178
  exc = sys.exc_info()[0]
183
179
  stack = traceback.extract_stack()[:-1]
184
180
  if exc is not None:
@@ -196,7 +192,7 @@ def _print_stacktrace(err: Optional[BaseException] = None) -> str:
196
192
  trc = 'Traceback (most recent call last):\n'
197
193
  stackstr = trc + ''.join(traceback.format_list(stack))
198
194
  if exc is not None:
199
- stackstr += ' ' + traceback.format_exc().lstrip(trc)
195
+ stackstr += ' ' + traceback.format_exc().lstrip(trc) # pylint:disable=bad-str-strip-call
200
196
  return stackstr
201
197
 
202
198
 
@@ -208,7 +204,11 @@ class DaraProdFormatter(logging.Formatter):
208
204
 
209
205
  @staticmethod
210
206
  def _get_payload(record: logging.LogRecord) -> Dict[str, JsonSerializable]:
211
- timestamp = time.strftime('%Y-%m-%dT%H:%M:%S', time.localtime(record.created)) + '.%s' % int(record.msecs)
207
+ timestamp = time.strftime(
208
+ '%Y-%m-%dT%H:%M:%S', time.localtime(record.created)
209
+ ) + '.%s' % int( # pylint:disable=consider-using-f-string
210
+ record.msecs
211
+ )
212
212
  if isinstance(record.msg, dict):
213
213
  payload: Dict[str, JsonSerializable] = {
214
214
  'timestamp': timestamp,
@@ -218,9 +218,8 @@ class DaraProdFormatter(logging.Formatter):
218
218
  }
219
219
 
220
220
  if record.levelname == 'ERROR':
221
- err = payload.pop('error')
222
- payload['error'] = str(err)
223
- payload['stacktrace'] = _print_stacktrace(err if isinstance(err, BaseException) else None)
221
+ payload['error'] = str(payload.pop('error'))
222
+ payload['stacktrace'] = _print_stacktrace()
224
223
 
225
224
  return payload
226
225
 
@@ -285,8 +284,8 @@ class DaraDevFormatter(logging.Formatter):
285
284
 
286
285
  if record.levelname == 'ERROR':
287
286
  error = ''
288
- if err := payload.get('error'):
289
- error = _print_stacktrace(err if isinstance(err, BaseException) else None)
287
+ if payload.get('error'):
288
+ error = _print_stacktrace()
290
289
  content = base_msg
291
290
  if record.__dict__.get('content'):
292
291
  content = content + '\r\n' + self.extra_template % (spacer, record.__dict__['content'])
dara/core/main.py CHANGED
@@ -14,7 +14,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
-
18
17
  import asyncio
19
18
  import os
20
19
  import sys
@@ -92,11 +91,10 @@ def _start_application(config: Configuration):
92
91
  os.environ['VITE_MANIFEST_PATH'] = f'{config.static_files_dir}/manifest.json'
93
92
  os.environ['VITE_STATIC_PATH'] = config.static_files_dir
94
93
  import fastapi_vite_dara
95
- import fastapi_vite_dara.config
96
94
 
97
95
  if len(config.pages) > 0:
98
96
  BASE_DIR = Path(__file__).parent
99
- jinja_templates = Jinja2Templates(directory=str(Path(BASE_DIR, 'jinja')))
97
+ jinja_templates = Jinja2Templates(directory=str((Path(BASE_DIR, 'jinja'))))
100
98
  jinja_templates.env.globals['vite_hmr_client'] = fastapi_vite_dara.vite_hmr_client
101
99
  jinja_templates.env.globals['vite_asset'] = fastapi_vite_dara.vite_asset
102
100
  jinja_templates.env.globals['static_url'] = fastapi_vite_dara.config.settings.static_url
@@ -109,7 +107,7 @@ def _start_application(config: Configuration):
109
107
 
110
108
  # Configure the default executor for threads run via the async loop
111
109
  loop = asyncio.get_event_loop()
112
- loop.set_default_executor(ThreadPoolExecutor(max_workers=int(os.environ.get('DARA_NUM_COMPONENT_THREADS', '8'))))
110
+ loop.set_default_executor(ThreadPoolExecutor(max_workers=int(os.environ.get('DARA_NUM_COMPONENT_THREADS', 8))))
113
111
 
114
112
  is_production = os.environ.get('DARA_DOCKER_MODE') == 'TRUE'
115
113
 
@@ -171,7 +169,7 @@ def _start_application(config: Configuration):
171
169
  worker_parameters={'task_module': config.task_module},
172
170
  max_workers=max_workers,
173
171
  )
174
- await task_pool.start(60) # timeout after 60s
172
+ await task_pool.start(60) # timeout after 60s
175
173
  utils_registry.set('TaskPool', task_pool)
176
174
  dev_logger.info('Task pool initialized')
177
175
 
@@ -339,15 +337,15 @@ def _start_application(config: Configuration):
339
337
 
340
338
  # Start metrics server in a daemon thread
341
339
  if os.environ.get('DARA_DISABLE_METRICS') != 'TRUE' and os.environ.get('DARA_TEST_FLAG', None) is None:
342
- port = int(os.environ.get('DARA_METRICS_PORT', '10000'))
340
+ port = int(os.environ.get('DARA_METRICS_PORT', 10000))
343
341
  start_http_server(port)
344
342
 
345
343
  # Start profiling server in a daemon thread if explicitly enabled (only works on linux)
346
344
  if os.environ.get('DARA_PYPPROF_PORT', None) is not None:
347
- profiling_port = int(os.environ.get('DARA_PYPPROF_PORT', '10001'))
345
+ profiling_port = int(os.environ.get('DARA_PYPPROF_PORT', 10001))
348
346
  dev_logger.warning('Starting cpu/memory profiling server', extra={'port': profiling_port})
349
347
 
350
- from pypprof.net_http import start_pprof_server # pyright: ignore[reportMissingImports]
348
+ from pypprof.net_http import start_pprof_server
351
349
 
352
350
  start_pprof_server(port=profiling_port)
353
351
 
@@ -360,7 +358,7 @@ def _start_application(config: Configuration):
360
358
  app.include_router(core_api_router, prefix='/api/core')
361
359
 
362
360
  @app.get('/api/{rest_of_path:path}')
363
- async def not_found():
361
+ async def not_found(): # pylint: disable=unused-variable
364
362
  raise HTTPException(status_code=404, detail='API endpoint not found')
365
363
 
366
364
  if len(config.pages) > 0:
@@ -371,23 +369,22 @@ def _start_application(config: Configuration):
371
369
  # Auto-js mode - serve the built template with UMDs
372
370
  if build_cache.build_config.mode == BuildMode.AUTO_JS:
373
371
  # Load template
374
- template_path = os.path.join(Path(BASE_DIR, 'jinja'), 'index_autojs.html') # type: ignore
375
- with open(template_path, encoding='utf-8') as fp:
372
+ with open(os.path.join(Path(BASE_DIR, 'jinja'), 'index_autojs.html'), 'r', encoding='utf-8') as fp:
376
373
  template = fp.read()
377
374
 
378
375
  # Generate tags for the template
379
376
  template = build_autojs_template(template, build_cache, config)
380
377
 
381
378
  @app.get('/{full_path:path}', include_in_schema=False, response_class=HTMLResponse)
382
- async def serve_app(request: Request): # pyright: ignore[reportRedeclaration]
379
+ async def serve_app(request: Request): # pylint: disable=unused-variable
383
380
  return HTMLResponse(template)
384
381
 
385
382
  else:
386
383
  # Otherwise serve the Vite template
387
384
 
388
385
  @app.get('/{full_path:path}', include_in_schema=False, response_class=_TemplateResponse)
389
- async def serve_app(request: Request): # pyright: ignore[reportRedeclaration]
390
- return jinja_templates.TemplateResponse(request, 'index.html') # type: ignore
386
+ async def serve_app(request: Request): # pylint: disable=unused-variable
387
+ return jinja_templates.TemplateResponse(request, 'index.html')
391
388
 
392
389
  return app
393
390
 
@@ -47,7 +47,7 @@ def format_bytes(num: Union[int, float]) -> str:
47
47
  # We only shrink the number if we HAVEN'T reached the last unit.
48
48
  num /= unit_step
49
49
 
50
- return f'{num:.2f} {unit}' # type: ignore
50
+ return f'{num:.2f} {unit}'
51
51
 
52
52
 
53
53
  class CacheMetricsTracker(BaseModel):
@@ -51,11 +51,11 @@ def total_size(o: object):
51
51
 
52
52
  try:
53
53
  all_handlers = {tuple: iter, list: iter, dict: dict_handler, set: iter, BaseModel: pydantic_handler}
54
- seen = set() # track which object id's have already been seen
55
- default_size = getsizeof(0) # estimate sizeof object without __sizeof__
54
+ seen = set() # track which object id's have already been seen
55
+ default_size = getsizeof(0) # estimate sizeof object without __sizeof__
56
56
 
57
57
  def sizeof(o):
58
- if id(o) in seen: # do not double count the same object
58
+ if id(o) in seen: # do not double count the same object
59
59
  return 0
60
60
  seen.add(id(o))
61
61
  s = getsizeof(o, default_size)
dara/core/persistence.py CHANGED
@@ -1,10 +1,10 @@
1
1
  import abc
2
2
  import json
3
3
  import os
4
- from collections.abc import Awaitable
5
4
  from typing import (
6
5
  TYPE_CHECKING,
7
6
  Any,
7
+ Awaitable,
8
8
  Callable,
9
9
  Dict,
10
10
  List,
@@ -255,7 +255,7 @@ class BackendStore(PersistenceStore):
255
255
  user = USER.get()
256
256
 
257
257
  if user:
258
- user_key = user.identity_id
258
+ user_key = user.identity_id or user.identity_name
259
259
 
260
260
  # Make sure the store is initialized
261
261
  if user_key not in self.initialized_scopes:
@@ -277,7 +277,7 @@ class BackendStore(PersistenceStore):
277
277
  if key == 'global':
278
278
  return None
279
279
 
280
- # otherwise key is a user identity_id
280
+ # otherwise key is a user identity_id or identity_name
281
281
  return key
282
282
 
283
283
  def _register(self):
@@ -374,7 +374,7 @@ class BackendStore(PersistenceStore):
374
374
  if not user:
375
375
  return
376
376
 
377
- user_identifier = user.identity_id
377
+ user_identifier = user.identity_id or user.identity_name
378
378
  return await self._notify_user(user_identifier, value=value, ignore_channel=ignore_channel)
379
379
 
380
380
  async def _notify_patches(self, patches: List[Dict[str, Any]]):
@@ -393,7 +393,7 @@ class BackendStore(PersistenceStore):
393
393
  if not user:
394
394
  return
395
395
 
396
- user_identifier = user.identity_id
396
+ user_identifier = user.identity_id or user.identity_name
397
397
  return await self._notify_user(user_identifier, patches=patches)
398
398
 
399
399
  async def init(self, variable: 'Variable'):
@@ -540,25 +540,3 @@ class BackendStoreEntry(BaseModel):
540
540
  uid: str
541
541
  store: BackendStore
542
542
  """Store instance"""
543
-
544
-
545
- class BrowserStore(PersistenceStore):
546
- """
547
- Persistence store implementation that uses browser local storage
548
- """
549
-
550
- async def init(self, variable: 'Variable'):
551
- # noop
552
- pass
553
-
554
-
555
- class QueryParamStore(PersistenceStore):
556
- """
557
- Persistence store implementation that uses a URL query parameter
558
- """
559
-
560
- query: str
561
-
562
- async def init(self, variable: 'Variable'):
563
- # noop
564
- pass