python-fx 0.3.2__py3-none-any.whl → 0.4.0__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.
- pyfx/__version__.py +1 -1
- pyfx/app.py +40 -7
- pyfx/model/model.py +7 -1
- pyfx/model/model_manager.py +210 -0
- pyfx/view/common/frame.py +1 -1
- pyfx/view/view_manager.py +2 -4
- {python_fx-0.3.2.dist-info → python_fx-0.4.0.dist-info}/METADATA +22 -22
- {python_fx-0.3.2.dist-info → python_fx-0.4.0.dist-info}/RECORD +12 -11
- {python_fx-0.3.2.dist-info → python_fx-0.4.0.dist-info}/WHEEL +1 -1
- {python_fx-0.3.2.dist-info → python_fx-0.4.0.dist-info}/entry_points.txt +0 -0
- {python_fx-0.3.2.dist-info → python_fx-0.4.0.dist-info/licenses}/LICENSE.txt +0 -0
- {python_fx-0.3.2.dist-info → python_fx-0.4.0.dist-info}/top_level.txt +0 -0
pyfx/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.4.0"
|
pyfx/app.py
CHANGED
|
@@ -10,7 +10,8 @@ from pyfx.config import parse
|
|
|
10
10
|
from pyfx.config import themes_path
|
|
11
11
|
from pyfx.config.config_parser import load
|
|
12
12
|
from pyfx.error import PyfxException
|
|
13
|
-
from pyfx.model import
|
|
13
|
+
from pyfx.model.model_manager import ModelManager
|
|
14
|
+
from pyfx.model.model_manager import ModelResult
|
|
14
15
|
from pyfx.service.client import Client
|
|
15
16
|
from pyfx.service.dispatcher import Dispatcher
|
|
16
17
|
from pyfx.view import View
|
|
@@ -53,10 +54,12 @@ class PyfxApp:
|
|
|
53
54
|
|
|
54
55
|
# backend part
|
|
55
56
|
self._dispatcher = Dispatcher()
|
|
56
|
-
# model
|
|
57
|
-
self.
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
# model manager
|
|
58
|
+
self._model_manager = ModelManager(
|
|
59
|
+
result_callback=self.__handle_model_result,
|
|
60
|
+
progress_callback=None)
|
|
61
|
+
self._dispatcher.register("query", self._model_manager.query)
|
|
62
|
+
self._dispatcher.register("complete", self._model_manager.complete)
|
|
60
63
|
|
|
61
64
|
# UI part
|
|
62
65
|
self._keymapper = self.__convert_keymap(self._config.ui.keymap)
|
|
@@ -194,9 +197,19 @@ class PyfxApp:
|
|
|
194
197
|
"exit with {}", e)
|
|
195
198
|
finally:
|
|
196
199
|
self._thread_pool_executor.shutdown(wait=True)
|
|
200
|
+
self._model_manager.shutdown(wait=True)
|
|
197
201
|
self._screen.clear()
|
|
198
202
|
|
|
199
|
-
def
|
|
203
|
+
def process_input(self, keys):
|
|
204
|
+
"""
|
|
205
|
+
Test-used method to process a list of keypress with proper model initialization
|
|
206
|
+
"""
|
|
207
|
+
init_success = self.__init(blocking=True)
|
|
208
|
+
if not init_success:
|
|
209
|
+
return False, "Model failed to load within timeout"
|
|
210
|
+
return self.__process_input(keys)
|
|
211
|
+
|
|
212
|
+
def __init(self, blocking=False):
|
|
200
213
|
"""Post-initializes Pyfx, it must be called before `__run()`.
|
|
201
214
|
|
|
202
215
|
.. note::
|
|
@@ -207,13 +220,22 @@ class PyfxApp:
|
|
|
207
220
|
processing data to construct essential widgets.
|
|
208
221
|
"""
|
|
209
222
|
logger.debug("Initializing Pyfx...")
|
|
210
|
-
|
|
223
|
+
# Start async model loading
|
|
224
|
+
self._model_manager.load(self._data)
|
|
225
|
+
if not blocking:
|
|
226
|
+
return True
|
|
227
|
+
return self._model_manager.wait_until_ready(timeout=5.0)
|
|
211
228
|
|
|
212
229
|
def __run(self):
|
|
213
230
|
"""Starts the UI loop."""
|
|
214
231
|
logger.debug("Running Pyfx...")
|
|
215
232
|
self._view.run()
|
|
216
233
|
|
|
234
|
+
def __process_input(self, keys):
|
|
235
|
+
"""Test-used method to process a list of keypress with proper model initialization."""
|
|
236
|
+
logger.debug("Running Pyfx...")
|
|
237
|
+
return self._view.process_input(keys)
|
|
238
|
+
|
|
217
239
|
def __init_logger(self, is_debug_mode):
|
|
218
240
|
logger.configure(
|
|
219
241
|
handlers=[{
|
|
@@ -266,3 +288,14 @@ class PyfxApp:
|
|
|
266
288
|
# avoid potential error during e2e test
|
|
267
289
|
pass
|
|
268
290
|
return screen
|
|
291
|
+
|
|
292
|
+
def __handle_model_result(self, result: ModelResult):
|
|
293
|
+
"""Handle async model results"""
|
|
294
|
+
if not result.success:
|
|
295
|
+
logger.error("Model operation failed: {}", result.error)
|
|
296
|
+
return
|
|
297
|
+
|
|
298
|
+
if result.operation_name == "Load":
|
|
299
|
+
logger.debug("Model loading completed, refreshing view")
|
|
300
|
+
# Use urwid alarm to safely update UI from background thread
|
|
301
|
+
self._mediator.notify('backend', 'refresh', 'json_browser', result.data)
|
pyfx/model/model.py
CHANGED
|
@@ -13,10 +13,16 @@ class Model:
|
|
|
13
13
|
* performs auto-completion with given JSONPath query
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
def __init__(self
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self._data = None
|
|
18
|
+
self._current = None
|
|
19
|
+
|
|
20
|
+
def load(self, data):
|
|
17
21
|
self._data = data
|
|
18
22
|
self._current = data
|
|
19
23
|
|
|
24
|
+
return self._current
|
|
25
|
+
|
|
20
26
|
def query(self, text):
|
|
21
27
|
if self._data is None:
|
|
22
28
|
logger.debug("Data is None.")
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import queue
|
|
3
|
+
from typing import Any, Optional, Callable
|
|
4
|
+
from concurrent.futures import ThreadPoolExecutor, Future
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from loguru import logger
|
|
8
|
+
|
|
9
|
+
from pyfx.model import Model
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ModelState(Enum):
|
|
13
|
+
CREATED = "created"
|
|
14
|
+
LOADING = "loading"
|
|
15
|
+
READY = "ready"
|
|
16
|
+
SHUTDOWN = "shutdown"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class ModelResult:
|
|
21
|
+
"""Result wrapper for threaded model operations"""
|
|
22
|
+
|
|
23
|
+
success: bool
|
|
24
|
+
operation_name: str
|
|
25
|
+
data: Any = None
|
|
26
|
+
error: Optional[str] = None
|
|
27
|
+
operation_id: Optional[str] = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ProgressUpdate:
|
|
32
|
+
"""Progress update for long-running operations"""
|
|
33
|
+
|
|
34
|
+
operation_id: str
|
|
35
|
+
progress: float # 0.0 to 1.0
|
|
36
|
+
message: str
|
|
37
|
+
completed: bool = False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ModelManager:
|
|
41
|
+
"""
|
|
42
|
+
Thread-safe wrapper around Model that runs operations in background thread.
|
|
43
|
+
|
|
44
|
+
Provides async interface for JSON processing while maintaining thread safety
|
|
45
|
+
between model operations and UI updates.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, result_callback, progress_callback, max_workers: int = 2):
|
|
49
|
+
self._executor = ThreadPoolExecutor(
|
|
50
|
+
max_workers=max_workers, thread_name_prefix="pyfx-model"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Thread-safe communication
|
|
54
|
+
self._result_queue = queue.Queue()
|
|
55
|
+
self._progress_queue = queue.Queue()
|
|
56
|
+
self._operation_counter = 0
|
|
57
|
+
self._operation_lock = threading.Lock()
|
|
58
|
+
|
|
59
|
+
# Callbacks for UI updates
|
|
60
|
+
self._result_callback: Optional[Callable[[ModelResult], None]] = result_callback
|
|
61
|
+
self._progress_callback: Optional[Callable[[ProgressUpdate], None]] = progress_callback
|
|
62
|
+
|
|
63
|
+
# Current operation tracking
|
|
64
|
+
self._current_futures: dict[str, Future] = {}
|
|
65
|
+
|
|
66
|
+
self._state = ModelState.CREATED
|
|
67
|
+
self._model: Model = Model()
|
|
68
|
+
|
|
69
|
+
def load(self, data: Any) -> str:
|
|
70
|
+
"""Load data asynchronously"""
|
|
71
|
+
|
|
72
|
+
def _load_task(operation_id, operation_name):
|
|
73
|
+
try:
|
|
74
|
+
self._report_progress(operation_id, 0.0, "Initializing model...")
|
|
75
|
+
|
|
76
|
+
self._state = ModelState.LOADING
|
|
77
|
+
result = self._model.load(data)
|
|
78
|
+
|
|
79
|
+
self._state = ModelState.READY
|
|
80
|
+
self._report_progress(operation_id, 1.0, "Model ready", completed=True)
|
|
81
|
+
|
|
82
|
+
model_result = ModelResult(
|
|
83
|
+
operation_id=operation_id,
|
|
84
|
+
operation_name=operation_name,
|
|
85
|
+
success=True,
|
|
86
|
+
data=result,
|
|
87
|
+
)
|
|
88
|
+
self._report_result(model_result)
|
|
89
|
+
except Exception as e:
|
|
90
|
+
logger.opt(exception=True).error("Load task failed: {}", e)
|
|
91
|
+
self._state = ModelState.SHUTDOWN
|
|
92
|
+
raise
|
|
93
|
+
|
|
94
|
+
return self._submit_task("Load", _load_task)
|
|
95
|
+
|
|
96
|
+
def query(self, text: str):
|
|
97
|
+
"""Execute JSONPath query synchronously"""
|
|
98
|
+
if self._state != ModelState.READY:
|
|
99
|
+
logger.warning(
|
|
100
|
+
"Model not ready for queries, current state: {}", self._state
|
|
101
|
+
)
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
return self._model.query(text)
|
|
105
|
+
|
|
106
|
+
def complete(self, text: str):
|
|
107
|
+
"""Execute autocompletion synchronously"""
|
|
108
|
+
if self._state != ModelState.READY:
|
|
109
|
+
logger.warning(
|
|
110
|
+
"Model not ready for completion, current state: {}", self._state
|
|
111
|
+
)
|
|
112
|
+
return False, "", []
|
|
113
|
+
|
|
114
|
+
return self._model.complete(text)
|
|
115
|
+
|
|
116
|
+
def cancel_operation(self, task_id: str) -> bool:
|
|
117
|
+
"""Cancel a running operation"""
|
|
118
|
+
if task_id not in self._current_futures:
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
future = self._current_futures[task_id]
|
|
122
|
+
if not future.cancel():
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
del self._current_futures[task_id]
|
|
126
|
+
return True
|
|
127
|
+
|
|
128
|
+
def wait_until_ready(self, timeout: float = 10.0) -> bool:
|
|
129
|
+
"""Block until model is ready or timeout occurs"""
|
|
130
|
+
import time
|
|
131
|
+
start_time = time.time()
|
|
132
|
+
|
|
133
|
+
while self._state != ModelState.READY:
|
|
134
|
+
if time.time() - start_time > timeout:
|
|
135
|
+
logger.warning("Model loading timeout after {}s", timeout)
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
time.sleep(0.01) # Small sleep to avoid busy waiting
|
|
139
|
+
|
|
140
|
+
return True
|
|
141
|
+
|
|
142
|
+
def shutdown(self, wait: bool = True):
|
|
143
|
+
"""Shutdown the model manager"""
|
|
144
|
+
self._state = ModelState.SHUTDOWN
|
|
145
|
+
|
|
146
|
+
# Cancel all pending operations
|
|
147
|
+
for operation_id in list(self._current_futures.keys()):
|
|
148
|
+
self.cancel_operation(operation_id)
|
|
149
|
+
|
|
150
|
+
# Shutdown executor
|
|
151
|
+
self._executor.shutdown(wait=wait)
|
|
152
|
+
|
|
153
|
+
def _submit_task(self, task_name, task):
|
|
154
|
+
task_id = self.__generate_task_id()
|
|
155
|
+
|
|
156
|
+
def wrapped_task():
|
|
157
|
+
try:
|
|
158
|
+
task(task_id, task_name)
|
|
159
|
+
except Exception as e:
|
|
160
|
+
logger.opt(exception=True).error("{} failed: {}", task_name, e)
|
|
161
|
+
self._report_error(
|
|
162
|
+
task_id, task_name, f"{task_name} failed: {str(e)}"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
future = self._executor.submit(wrapped_task)
|
|
166
|
+
self._current_futures[task_id] = future
|
|
167
|
+
return task_id
|
|
168
|
+
|
|
169
|
+
def __generate_task_id(self) -> str:
|
|
170
|
+
"""Generate unique operation ID"""
|
|
171
|
+
with self._operation_lock:
|
|
172
|
+
self._operation_counter += 1
|
|
173
|
+
return f"op_{self._operation_counter}"
|
|
174
|
+
|
|
175
|
+
def _report_progress(
|
|
176
|
+
self, operation_id: str, progress: float, message: str, completed: bool = False
|
|
177
|
+
):
|
|
178
|
+
"""Report progress update"""
|
|
179
|
+
update = ProgressUpdate(
|
|
180
|
+
operation_id=operation_id,
|
|
181
|
+
progress=progress,
|
|
182
|
+
message=message,
|
|
183
|
+
completed=completed,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
try:
|
|
187
|
+
self._progress_queue.put_nowait(update)
|
|
188
|
+
if self._progress_callback:
|
|
189
|
+
self._progress_callback(update)
|
|
190
|
+
except queue.Full:
|
|
191
|
+
logger.warning("Progress queue full, dropping update")
|
|
192
|
+
|
|
193
|
+
def _report_result(self, result: ModelResult):
|
|
194
|
+
"""Report operation result"""
|
|
195
|
+
try:
|
|
196
|
+
self._result_queue.put_nowait(result)
|
|
197
|
+
if self._result_callback:
|
|
198
|
+
self._result_callback(result)
|
|
199
|
+
except queue.Full:
|
|
200
|
+
logger.warning("Result queue full, dropping result")
|
|
201
|
+
|
|
202
|
+
def _report_error(self, operation_id: str, operation_name: str, error_msg: str):
|
|
203
|
+
"""Report operation error"""
|
|
204
|
+
result = ModelResult(
|
|
205
|
+
operation_id=operation_id,
|
|
206
|
+
success=False,
|
|
207
|
+
operation_name=operation_name,
|
|
208
|
+
error=error_msg,
|
|
209
|
+
)
|
|
210
|
+
self._report_result(result)
|
pyfx/view/common/frame.py
CHANGED
pyfx/view/view_manager.py
CHANGED
|
@@ -48,11 +48,9 @@ class View:
|
|
|
48
48
|
for index, key in enumerate(keys):
|
|
49
49
|
# work around for urwid.MainLoop#process_input does not apply
|
|
50
50
|
# input filter
|
|
51
|
-
key = self._loop.input_filter([key],
|
|
51
|
+
key = self._loop.input_filter([key], [])
|
|
52
52
|
|
|
53
|
-
if len(key) == 0:
|
|
54
|
-
continue
|
|
55
|
-
elif self._loop.process_input(key):
|
|
53
|
+
if len(key) == 0 or self._loop.process_input(key):
|
|
56
54
|
continue
|
|
57
55
|
|
|
58
56
|
return False, f"keys[{index}]: {key} is not handled"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-fx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A python-native fx-alike terminal JSON viewer.
|
|
5
5
|
Author-email: Yutian Wu <yutianwu@umich.edu>
|
|
6
6
|
License: MIT
|
|
@@ -13,32 +13,32 @@ Classifier: Operating System :: POSIX
|
|
|
13
13
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
15
|
Classifier: Topic :: Utilities
|
|
16
|
-
Requires-Python: >=3.
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
License-File: LICENSE.txt
|
|
19
|
-
Requires-Dist: antlr4-python3-runtime
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist: loguru
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
Requires-Dist:
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist: pyyaml
|
|
32
|
-
Requires-Dist: typing-extensions
|
|
33
|
-
Requires-Dist: urwid
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
19
|
+
Requires-Dist: antlr4-python3-runtime<5,>=4.13
|
|
20
|
+
Requires-Dist: asciimatics<2,>=1.15
|
|
21
|
+
Requires-Dist: click<9,>=8.1.7
|
|
22
|
+
Requires-Dist: first<3,==2.0
|
|
23
|
+
Requires-Dist: dacite<2,==1.8
|
|
24
|
+
Requires-Dist: jsonpath-ng<2,==1.6
|
|
25
|
+
Requires-Dist: loguru<0.8,>=0.7.2
|
|
26
|
+
Requires-Dist: overrides<8,>=7.7.0
|
|
27
|
+
Requires-Dist: pillow<11,>=10.4
|
|
28
|
+
Requires-Dist: ply<4,>=3.11
|
|
29
|
+
Requires-Dist: pyfiglet<2,>=1.0
|
|
30
|
+
Requires-Dist: pyperclip>=1.9
|
|
31
|
+
Requires-Dist: pyyaml<7,>=6.0.2
|
|
32
|
+
Requires-Dist: typing-extensions<5,>=4.12.2
|
|
33
|
+
Requires-Dist: urwid<3,>=2.6
|
|
34
|
+
Requires-Dist: wcwidth<0.3,>=0.2.13
|
|
35
|
+
Requires-Dist: yamale<6,>=5.2
|
|
36
|
+
Dynamic: license-file
|
|
36
37
|
|
|
37
38
|
# Pyfx
|
|
38
39
|

|
|
39
40
|

|
|
40
41
|

|
|
41
|
-

|
|
42
42
|

|
|
43
43
|

|
|
44
44
|

|
|
@@ -69,7 +69,7 @@ A python-native JSON Viewer TUI, inspired by [fx](https://github.com/antonmedv/f
|
|
|
69
69
|
|
|
70
70
|
## Prerequisites
|
|
71
71
|
* OS: MacOS / Linux
|
|
72
|
-
* python: >= 3.
|
|
72
|
+
* python: >= 3.9
|
|
73
73
|
* pip
|
|
74
74
|
|
|
75
75
|
## Installation
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
pyfx/__init__.py,sha256=KU0-7SWHjuFrvQ3UJHeUcUG41-IpwGBSCjIibDD52H8,25
|
|
2
|
-
pyfx/__version__.py,sha256=
|
|
3
|
-
pyfx/app.py,sha256=
|
|
2
|
+
pyfx/__version__.py,sha256=42STGor_9nKYXumfeV5tiyD_M8VdcddX7CEexmibPBk,22
|
|
3
|
+
pyfx/app.py,sha256=WwpjxneQY0lBf6tI9l9HdA2LjUmsLyav9f9tfmX__L4,12143
|
|
4
4
|
pyfx/cli.py,sha256=9mi8kOiZFcY4RCN1TUV5aCeKmuoDwZm-1kVRdF7vbNE,2135
|
|
5
5
|
pyfx/cli_utils.py,sha256=TLF-242Twi54i8URozwxmZVfEibvmwDWDE5AFyWqC58,1243
|
|
6
6
|
pyfx/error.py,sha256=9AQ7q3zJQJEvvWGv_39Ep17KoN3eSiRgeqhh4_-7Mqg,96
|
|
@@ -17,7 +17,8 @@ pyfx/config/yaml/keymaps/vim.yml,sha256=sNW6AsUXHxUNrF9u_EXXlU92WYbay1X94XfcA4ZK
|
|
|
17
17
|
pyfx/config/yaml/themes/__init__.py,sha256=CY_rp0Unst3ZQ9npKe6cxdwrs9yrbsMdeQGH-q2W7iM,30
|
|
18
18
|
pyfx/config/yaml/themes/basic.yml,sha256=lffeUU4ABZ57-eL7-tXTCuXqtqrXDv12V_Qk0AmhpxQ,1217
|
|
19
19
|
pyfx/model/__init__.py,sha256=TrL7n4E7ZA7EmNhk6Pp-YtMPxOl90vyEd9jymd143eI,246
|
|
20
|
-
pyfx/model/model.py,sha256
|
|
20
|
+
pyfx/model/model.py,sha256=-pIOgdP32dTA3mzjSVRlmLoyhvRQbISa5FGlrmus1nU,1330
|
|
21
|
+
pyfx/model/model_manager.py,sha256=YmKXLxsj300_u3CEwH98KjX725zToUKxteX8CyiFNj4,6614
|
|
21
22
|
pyfx/model/autocomplete/__init__.py,sha256=PPKRsvPcvJQcJL-Y74BEs0_26haxlnvnBxa_sqbXF6U,517
|
|
22
23
|
pyfx/model/autocomplete/autocomplete_listener.py,sha256=SrQ5WlMfSVwcjOPf08_bZUqNya5hX3JNefkYB0SNbqk,11667
|
|
23
24
|
pyfx/model/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -37,10 +38,10 @@ pyfx/view/__init__.py,sha256=XZG19brgFnZcARuPifl9PqwNXgPc3drJHq6NwNjol3s,198
|
|
|
37
38
|
pyfx/view/keys.py,sha256=GgW1yX68OscZr_WPzzc8MnsUmFo7TKbV4BYvI78FgTc,3242
|
|
38
39
|
pyfx/view/themes.py,sha256=mXo2_ywvMxAedj8a2LzbGIyP2mWt8zZSjvfqrYqWEhk,1840
|
|
39
40
|
pyfx/view/view_frame.py,sha256=zdSgN_CYGmxp2PgWM5q8qPOGVWcprmy6-ugsbswX-yQ,2476
|
|
40
|
-
pyfx/view/view_manager.py,sha256
|
|
41
|
+
pyfx/view/view_manager.py,sha256=-jftt-aoKBjqTyw41X0XQYNZJNrIbBP36VwNFI14nr8,1806
|
|
41
42
|
pyfx/view/view_mediator.py,sha256=mY1-IT77Xi7DwtLW-hyrSE4NdFCPNXffnaFHztnL53s,1137
|
|
42
43
|
pyfx/view/common/__init__.py,sha256=yD8y941cJ_fR8XYbSmKzOIAUhRt3eSA60pU1PRYfyZY,137
|
|
43
|
-
pyfx/view/common/frame.py,sha256=
|
|
44
|
+
pyfx/view/common/frame.py,sha256=7IjKe6-BOc6CGlj6lL2EfC5z7bSnGhSTVtc1DxhISRk,11596
|
|
44
45
|
pyfx/view/common/popup.py,sha256=P6MyMMpR_Mvqz7SvsTQN-wjIe2DgFeomSy4j4X3s0bU,1835
|
|
45
46
|
pyfx/view/common/selectable_text.py,sha256=vg70CrO8495RztSsdw45JDgtH5zBQAO7DesHSiQ9yqg,490
|
|
46
47
|
pyfx/view/components/__init__.py,sha256=W1jwydHqN8Yfbh46sujZnNML2Pfs7opfNe0edADwh5U,352
|
|
@@ -78,9 +79,9 @@ pyfx/view/json_lib/primitive/integer.py,sha256=C-xOeR3deeVobAkvEoIfGYFu0koNIMsGk
|
|
|
78
79
|
pyfx/view/json_lib/primitive/null.py,sha256=BL9CuFTGmHt6tmXjIvuOwxIEwpesp0Df4J88aMj8rgM,775
|
|
79
80
|
pyfx/view/json_lib/primitive/numeric.py,sha256=DYI8eZULo2gKSeyzDtnXk4fgtgBYyoVH_ARGaEJ5YXI,868
|
|
80
81
|
pyfx/view/json_lib/primitive/string.py,sha256=oBFzZKhrKoKLLdiVg6ivkryB2cJDDs34B-xGXf63F9E,839
|
|
81
|
-
python_fx-0.
|
|
82
|
-
python_fx-0.
|
|
83
|
-
python_fx-0.
|
|
84
|
-
python_fx-0.
|
|
85
|
-
python_fx-0.
|
|
86
|
-
python_fx-0.
|
|
82
|
+
python_fx-0.4.0.dist-info/licenses/LICENSE.txt,sha256=KxdNyEebr0IyALPixRsvD-2puJr9WxSePEe1UsjxPlA,1066
|
|
83
|
+
python_fx-0.4.0.dist-info/METADATA,sha256=tCtrBPSiRQ6ni1ne87Gi0kb9LYmn5aSGMcLH4q9Cry8,10060
|
|
84
|
+
python_fx-0.4.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
85
|
+
python_fx-0.4.0.dist-info/entry_points.txt,sha256=8Veumj2a-mijrFP-07Rph2IRrR78XjTLLFc_gD1U-mE,39
|
|
86
|
+
python_fx-0.4.0.dist-info/top_level.txt,sha256=X4zR7lGFzBoSu_P2ocvTC0t50kHygVjqbMA4twR1WZ0,5
|
|
87
|
+
python_fx-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|