blissdata 0.3.4__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 (67) hide show
  1. blissdata/__init__.py +17 -0
  2. blissdata/beacon/__init__.py +1 -0
  3. blissdata/beacon/_base.py +106 -0
  4. blissdata/beacon/config.py +24 -0
  5. blissdata/beacon/data.py +45 -0
  6. blissdata/beacon/files.py +141 -0
  7. blissdata/client.py +62 -0
  8. blissdata/common/__init__.py +15 -0
  9. blissdata/common/utils.py +159 -0
  10. blissdata/data/__init__.py +15 -0
  11. blissdata/data/events/__init__.py +22 -0
  12. blissdata/data/events/channel.py +149 -0
  13. blissdata/data/events/lima.py +478 -0
  14. blissdata/data/events/node.py +28 -0
  15. blissdata/data/events/scan.py +49 -0
  16. blissdata/data/events/walk.py +42 -0
  17. blissdata/data/expiration.py +42 -0
  18. blissdata/data/lima_image.py +465 -0
  19. blissdata/data/node.py +1619 -0
  20. blissdata/data/nodes/__init__.py +11 -0
  21. blissdata/data/nodes/channel.py +394 -0
  22. blissdata/data/nodes/dataset.py +82 -0
  23. blissdata/data/nodes/dataset_collection.py +12 -0
  24. blissdata/data/nodes/lima.py +422 -0
  25. blissdata/data/nodes/node_ref_channel.py +34 -0
  26. blissdata/data/nodes/proposal.py +12 -0
  27. blissdata/data/nodes/scan.py +192 -0
  28. blissdata/data/nodes/scan_group.py +12 -0
  29. blissdata/data/remote_node.py +204 -0
  30. blissdata/data/scan.py +666 -0
  31. blissdata/h5api/__init__.py +1 -0
  32. blissdata/h5api/abstract.py +97 -0
  33. blissdata/h5api/dynamic_hdf5.py +153 -0
  34. blissdata/h5api/file_arguments.py +28 -0
  35. blissdata/h5api/static_hdf5.py +139 -0
  36. blissdata/h5api/utils/__init__.py +0 -0
  37. blissdata/h5api/utils/bliss.py +138 -0
  38. blissdata/h5api/utils/hdf5.py +280 -0
  39. blissdata/h5api/utils/hdf5_retry.py +98 -0
  40. blissdata/h5api/utils/lima.py +286 -0
  41. blissdata/h5api/utils/types.py +13 -0
  42. blissdata/redis/__init__.py +12 -0
  43. blissdata/redis/caching.py +390 -0
  44. blissdata/redis/connection.py +169 -0
  45. blissdata/redis/manager.py +164 -0
  46. blissdata/redis/proxy.py +971 -0
  47. blissdata/redis/scripting.py +24 -0
  48. blissdata/settings.py +1174 -0
  49. blissdata/streaming.py +819 -0
  50. blissdata/streaming_events.py +355 -0
  51. blissdata/tests/__init__.py +0 -0
  52. blissdata/tests/beacon/__init__.py +0 -0
  53. blissdata/tests/beacon/test_config.py +26 -0
  54. blissdata/tests/beacon/test_data.py +50 -0
  55. blissdata/tests/beacon/test_files.py +80 -0
  56. blissdata/tests/conftest.py +0 -0
  57. blissdata/tests/h5api/__init__.py +0 -0
  58. blissdata/tests/h5api/scanner.py +390 -0
  59. blissdata/tests/h5api/test_dynamic_files.py +309 -0
  60. blissdata/tests/h5api/test_static_files.py +169 -0
  61. blissdata/tests/redis/__init__.py +0 -0
  62. blissdata/tests/redis/manager.py +23 -0
  63. blissdata-0.3.4.dist-info/LICENSE +165 -0
  64. blissdata-0.3.4.dist-info/METADATA +79 -0
  65. blissdata-0.3.4.dist-info/RECORD +67 -0
  66. blissdata-0.3.4.dist-info/WHEEL +5 -0
  67. blissdata-0.3.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,164 @@
1
+ """Top-level API to Redis
2
+ """
3
+
4
+ import os
5
+ import socket
6
+ from typing import Dict, Optional, Set
7
+ import weakref
8
+ from dataclasses import dataclass
9
+ import gevent.lock
10
+
11
+ from blissdata.redis.connection import create_connection_pool
12
+ from blissdata.redis.connection import RedisDbConnectionPool
13
+ from blissdata.redis.proxy import RedisDbProxyBase
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class RedisPoolId:
18
+ db: int
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class RedisProxyId:
23
+ db: int
24
+ caching: bool
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class RedisAddress:
29
+ host: str
30
+ port: Optional[int]
31
+ sock: Optional[str]
32
+
33
+ @classmethod
34
+ def factory(cls, address: Optional[str] = None):
35
+ host = None
36
+ port = None
37
+ sock = None
38
+ DEFAULT_REDIS_PORT = 6379
39
+ if address is None:
40
+ address = os.environ.get("REDIS_HOST")
41
+ if address is None:
42
+ host = "localhost"
43
+ port = DEFAULT_REDIS_PORT
44
+ elif ":" in address:
45
+ host, suffix = address.split(":", 1)
46
+ try:
47
+ port = int(suffix)
48
+ except ValueError:
49
+ sock = suffix
50
+ else:
51
+ host = address
52
+ port = DEFAULT_REDIS_PORT
53
+ return cls(host=host, port=port, sock=sock)
54
+
55
+ @property
56
+ def url(self):
57
+ if self.sock is not None:
58
+ return f"unix://{self.sock}"
59
+ else:
60
+ return f"redis://{self.host}:{self.port}"
61
+
62
+
63
+ class RedisConnectionManager:
64
+ """
65
+ Use `get_db_proxy` to create a connection or use an existing one.
66
+ Use `close_all_connections` to close all Redis connections.
67
+ """
68
+
69
+ CLIENT_NAME = f"{socket.gethostname()}:{os.getpid()}"
70
+
71
+ def __init__(self, addresses: Dict[int, RedisAddress]):
72
+ self._addresses = addresses
73
+
74
+ self._get_proxy_lock = gevent.lock.Semaphore()
75
+
76
+ # Keep hard references to all shared Redis proxies
77
+ # (these proxies don't hold a `redis.Redis.Connection` instance)
78
+ self._shared_proxies: Dict[RedisProxyId, RedisDbProxyBase] = dict()
79
+
80
+ # Keep weak references to all shared Redis connection pools:
81
+ self._connection_pools: Dict[
82
+ RedisPoolId, RedisDbConnectionPool
83
+ ] = weakref.WeakValueDictionary()
84
+
85
+ # Keep weak references to all cached Redis proxies which are not
86
+ # reused (although they could be but their cache with kep growing)
87
+ self._non_shared_proxies: Set[RedisDbProxyBase] = weakref.WeakSet()
88
+
89
+ # Hard references to the connection pools are held by the
90
+ # Redis proxies themselves. Connections of RedisDbConnectionPool
91
+ # are closed upon garbage collection of RedisDbConnectionPool. So
92
+ # when the proxies too a pool are the only ones having a hard
93
+ # reference too that pool, the connections are closed when all
94
+ # proxies are garbage collected.
95
+
96
+ def get_db_proxy(
97
+ self, db: int = 0, caching: bool = False, shared: bool = True
98
+ ) -> RedisDbProxyBase:
99
+ """Get a greenlet-safe proxy to a Redis database.
100
+
101
+ :param int db: Redis database too which we need a proxy
102
+ :param bool caching: client-side caching
103
+ :param bool shared: use a shared proxy held by the Beacon connection
104
+ """
105
+ proxyid = RedisProxyId(db=db, caching=caching)
106
+ if shared:
107
+ return self._get_shared_db_proxy(proxyid)
108
+ else:
109
+ return self._get_non_shared_db_proxy(proxyid)
110
+
111
+ def close_all_connections(self):
112
+ # To close `redis.connection.Connection` you need to call its
113
+ # `disconnect` method (also called on garbage collection).
114
+ #
115
+ # Connection pools have a `disconnect` method that disconnect
116
+ # all their connections, which means close and destroy their
117
+ # socket instances.
118
+ #
119
+ # Note: closing a proxy will not close any connections
120
+ proxies = list(self._non_shared_proxies)
121
+ proxies.extend(self._shared_proxies.values())
122
+ self._shared_proxies = dict()
123
+ self._non_shared_proxies = weakref.WeakSet()
124
+ for proxy in proxies:
125
+ proxy.close()
126
+ proxy.connection_pool.disconnect()
127
+
128
+ def _get_shared_db_proxy(self, proxyid: RedisProxyId) -> RedisDbProxyBase:
129
+ """Get a reusabed proxy and create it when it doesn't exist."""
130
+ with self._get_proxy_lock:
131
+ proxy = self._shared_proxies.get(proxyid)
132
+ if proxy is None:
133
+ pool = self._get_connection_pool(proxyid)
134
+ proxy = pool.create_proxy(caching=proxyid.caching)
135
+ self._shared_proxies[proxyid] = proxy
136
+ return proxy
137
+
138
+ def _get_non_shared_db_proxy(self, proxyid: RedisProxyId) -> RedisDbProxyBase:
139
+ """Get a reusabed proxy and create it when it doesn't exist."""
140
+ with self._get_proxy_lock:
141
+ pool = self._get_connection_pool(proxyid)
142
+ proxy = pool.create_proxy(caching=proxyid.caching)
143
+ self._non_shared_proxies.add(proxy)
144
+ return proxy
145
+
146
+ def _get_connection_pool(self, proxyid: RedisProxyId) -> RedisDbConnectionPool:
147
+ """Get a Redis connection pool (create when it does not exist yet)
148
+ for the db.
149
+ """
150
+ poolid = RedisPoolId(db=proxyid.db)
151
+ pool = self._connection_pools.get(poolid)
152
+ if pool is None:
153
+ pool = create_connection_pool(
154
+ self._get_url(poolid.db), poolid.db, client_name=self.CLIENT_NAME
155
+ )
156
+ self._connection_pools[poolid] = pool
157
+ return pool
158
+
159
+ def _get_url(self, db: int) -> str:
160
+ """Full Redis URL as a string"""
161
+ address = self._addresses.get(db)
162
+ if address is None:
163
+ raise RuntimeError(f"No Redis address specified for database {db}")
164
+ return address.url