pyglove 0.4.5.dev202412080808__py3-none-any.whl → 0.4.5.dev202412100810__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.
@@ -352,7 +352,7 @@ class Html(base.Content):
352
352
  cls,
353
353
  s: WritableTypes,
354
354
  javascript_str: bool = False
355
- ) -> Union[str, 'Html', None]:
355
+ ) -> Any:
356
356
  """Escapes an HTML writable object."""
357
357
  if s is None:
358
358
  return None
@@ -62,6 +62,7 @@ class HtmlControl(pg_object.Object):
62
62
  super()._on_bound()
63
63
  self._rendered = False
64
64
  self._css_styles = []
65
+ self._dynamic_injected_css = set()
65
66
  self._scripts = []
66
67
 
67
68
  def add_style(self, *css: str) -> 'HtmlControl':
@@ -76,6 +77,7 @@ class HtmlControl(pg_object.Object):
76
77
  def to_html(self, **kwargs) -> Html:
77
78
  """Returns the HTML representation of the control."""
78
79
  self._rendered = True
80
+ self._dynamic_injected_css = set()
79
81
  html = self._to_html(**kwargs)
80
82
  return html.add_style(*self._css_styles).add_script(*self._scripts)
81
83
 
@@ -102,7 +104,7 @@ class HtmlControl(pg_object.Object):
102
104
  """Synchronizes displayed values to members."""
103
105
  self.rebind(fields, skip_notification=True, raise_on_no_change=False)
104
106
 
105
- def _run_javascript(self, code: str) -> None:
107
+ def _run_javascript(self, code: str, debug: bool = False) -> None:
106
108
  """Runs the given JavaScript code."""
107
109
  if not self.interactive:
108
110
  raise ValueError(
@@ -113,6 +115,8 @@ class HtmlControl(pg_object.Object):
113
115
  return
114
116
 
115
117
  code = inspect.cleandoc(code)
118
+ if debug:
119
+ print('RUN JAVSCRIPT:\n', code)
116
120
  if _notebook is not None:
117
121
  _notebook.display(_notebook.Javascript(code))
118
122
 
@@ -121,6 +125,40 @@ class HtmlControl(pg_object.Object):
121
125
  for tracked in all_tracked:
122
126
  tracked.append(code)
123
127
 
128
+ def _add_css_rules(self, css: str) -> None:
129
+ if not self._rendered or not css or css in self._dynamic_injected_css:
130
+ return
131
+ self._run_javascript(
132
+ f"""
133
+ const style = document.createElement('style');
134
+ style.type = 'text/css';
135
+ style.textContent = "{Html.escape(css, javascript_str=True)}";
136
+ document.head.appendChild(style);
137
+ """
138
+ )
139
+ self._dynamic_injected_css.add(css)
140
+
141
+ def _apply_css_rules(self, html: Html) -> None:
142
+ self._add_css_rules(html.styles.content)
143
+
144
+ def _insert_adjacent_html(
145
+ self,
146
+ element_selector_js: str,
147
+ html: Html,
148
+ var_name: str = 'elem',
149
+ position: str = 'beforeend'
150
+ ):
151
+ self._run_javascript(
152
+ f"""
153
+ {element_selector_js}
154
+ {var_name}.insertAdjacentHTML(
155
+ "{position}",
156
+ "{Html.escape(html, javascript_str=True).to_str(content_only=True)}"
157
+ );
158
+ """,
159
+ )
160
+ self._apply_css_rules(html)
161
+
124
162
  def element_id(self, child: Optional[str] = None) -> Optional[str]:
125
163
  """Returns the element id of this control or a child."""
126
164
  if self.id is not None:
@@ -163,14 +201,13 @@ class HtmlControl(pg_object.Object):
163
201
  child: Optional[str] = None,
164
202
  ) -> base.Html:
165
203
  """Updates the inner HTML of the control."""
166
- js_html = Html.escape(html, javascript_str=True)
167
- assert isinstance(js_html, Html), js_html
168
204
  self._run_javascript(
169
205
  f"""
170
206
  elem = document.getElementById("{self.element_id(child)}");
171
- elem.innerHTML = "{js_html.to_str(content_only=True)}";
207
+ elem.innerHTML = "{Html.escape(html, javascript_str=True).to_str(content_only=True)}";
172
208
  """
173
209
  )
210
+ self._add_css_rules(html.styles.content)
174
211
  return html
175
212
 
176
213
  def _update_style(
@@ -96,6 +96,8 @@ class Label(HtmlControl):
96
96
  tooltip: Union[str, Html, None] = None,
97
97
  link: Optional[str] = None,
98
98
  styles: Optional[Dict[str, Any]] = None,
99
+ add_class: Optional[List[str]] = None,
100
+ remove_class: Optional[List[str]] = None,
99
101
  ) -> None:
100
102
  if text is not None:
101
103
  self._sync_members(text=self._update_content(text))
@@ -105,7 +107,16 @@ class Label(HtmlControl):
105
107
  self._sync_members(link=self._update_property('href', link))
106
108
  if tooltip is not None:
107
109
  self.tooltip.update(content=tooltip)
108
-
110
+ if add_class or remove_class:
111
+ css_classes = list(self.css_classes)
112
+ for x in add_class or []:
113
+ self._add_css_class(x)
114
+ css_classes.append(x)
115
+ for x in remove_class or []:
116
+ self._remove_css_class(x)
117
+ if x in css_classes:
118
+ css_classes.remove(x)
119
+ self._sync_members(css_classes=css_classes)
109
120
 
110
121
  # Register converter for automatic conversion.
111
122
  pg_typing.register_converter(str, Label, Label)
@@ -60,19 +60,26 @@ class LabelTest(TestCase):
60
60
  )
61
61
 
62
62
  def test_update(self):
63
- label = label_lib.Label('foo', 'bar', 'http://google.com', interactive=True)
63
+ label = label_lib.Label(
64
+ 'foo', 'bar', 'http://google.com',
65
+ interactive=True,
66
+ css_classes=['foo', 'bar'],
67
+ )
64
68
  self.assertIn('id="control-', label.to_html_str(content_only=True))
65
69
  with label.track_scripts() as scripts:
66
70
  label.update(
67
71
  'bar',
68
72
  tooltip='baz',
69
73
  link='http://www.yahoo.com',
70
- styles=dict(color='red')
74
+ styles=dict(color='red'),
75
+ add_class=['baz'],
76
+ remove_class=['bar'],
71
77
  )
72
78
  self.assertEqual(label.text, 'bar')
73
79
  self.assertEqual(label.tooltip.content, 'baz')
74
80
  self.assertEqual(label.link, 'http://www.yahoo.com')
75
81
  self.assertEqual(label.styles, dict(color='red'))
82
+ self.assertEqual(label.css_classes, ['foo', 'baz'])
76
83
  self.assertEqual(
77
84
  scripts,
78
85
  [
@@ -106,6 +113,18 @@ class LabelTest(TestCase):
106
113
  elem.classList.remove("html-content");
107
114
  """
108
115
  ),
116
+ inspect.cleandoc(
117
+ f"""
118
+ elem = document.getElementById("{label.element_id()}");
119
+ elem.classList.add("baz");
120
+ """
121
+ ),
122
+ inspect.cleandoc(
123
+ f"""
124
+ elem = document.getElementById("{label.element_id()}");
125
+ elem.classList.remove("bar");
126
+ """
127
+ ),
109
128
  ]
110
129
  )
111
130
 
@@ -15,7 +15,9 @@
15
15
 
16
16
  from typing import Annotated, List, Literal, Union
17
17
 
18
+ from pyglove.core.symbolic import flags as pg_flags
18
19
  from pyglove.core.symbolic import object as pg_object
20
+
19
21
  # pylint: disable=g-importing-member
20
22
  from pyglove.core.views.html.base import Html
21
23
  from pyglove.core.views.html.base import HtmlConvertible
@@ -37,6 +39,11 @@ class Tab(pg_object.Object):
37
39
  'The content of the tab.'
38
40
  ]
39
41
 
42
+ css_classes: Annotated[
43
+ List[str],
44
+ 'The CSS classes of the tab.'
45
+ ] = []
46
+
40
47
 
41
48
  @pg_object.use_init_args(
42
49
  ['tabs', 'selected', 'tab_position', 'id', 'css_classes', 'styles']
@@ -61,109 +68,175 @@ class TabControl(HtmlControl):
61
68
 
62
69
  interactive = True
63
70
 
71
+ def append(self, tab: Tab) -> None:
72
+ with pg_flags.notify_on_change(False):
73
+ self.tabs.append(tab)
74
+
75
+ self._insert_adjacent_html(
76
+ f"""
77
+ const elem = document.getElementById('{self.element_id()}-button-group');
78
+ """,
79
+ self._tab_button(tab, len(self.tabs) - 1),
80
+ position='beforeend',
81
+ )
82
+ self._insert_adjacent_html(
83
+ f"""
84
+ const elem = document.getElementById('{self.element_id()}-content-group');
85
+ """,
86
+ self._tab_content(tab, len(self.tabs) - 1),
87
+ position='beforeend',
88
+ )
89
+
90
+ def extend(self, tabs: List[Tab]) -> None:
91
+ for tab in tabs:
92
+ self.append(tab)
93
+
64
94
  def _to_html(self, **kwargs):
65
95
  return Html.element(
66
- 'div',
96
+ 'table',
67
97
  [
98
+ '<tr><td>',
68
99
  Html.element(
69
100
  'div',
70
- [
71
- Html.element(
72
- 'button',
73
- [
74
- tab.label
75
- ],
76
- css_classes=[
77
- 'tab-button',
78
- 'selected' if i == self.selected else None
79
- ],
80
- onclick=(
81
- f"""openTab(event, '{self.element_id()}', '{self.element_id(str(i))}')"""
82
- )
83
- ) for i, tab in enumerate(self.tabs)
84
- ],
85
- css_classes=['tab-button-group'],
101
+ [self._tab_button(tab, i) for i, tab in enumerate(self.tabs)],
102
+ css_classes=[
103
+ 'tab-button-group',
104
+ self.tab_position
105
+ ] + self.css_classes,
106
+ id=self.element_id('button-group'),
86
107
  ),
87
- ] + [
108
+ ('</td><td>' if self.tab_position == 'left'
109
+ else '</td></tr><tr><td>'),
88
110
  Html.element(
89
111
  'div',
90
- [
91
- tab.content
92
- ],
93
- css_classes=['tab-content'],
94
- styles=dict(
95
- display='block' if i == self.selected else 'none'
96
- ),
97
- id=self.element_id(str(i))
98
- ) for i, tab in enumerate(self.tabs)
112
+ [self._tab_content(tab, i) for i, tab in enumerate(self.tabs)],
113
+ css_classes=[
114
+ 'tab-content-group',
115
+ self.tab_position
116
+ ] + self.css_classes,
117
+ id=self.element_id('content-group'),
118
+ ),
119
+ '</td></tr>'
99
120
  ],
100
- css_classes=['tab-control', self.tab_position] + self.css_classes,
101
- id=self.element_id(),
121
+ css_classes=['tab-control'],
102
122
  styles=self.styles,
103
123
  ).add_script(
104
124
  """
105
125
  function openTab(event, controlId, tabId) {
106
- const tabButtons = document.querySelectorAll('#' + controlId + '> .tab-button-group > .tab-button');
126
+ const tabButtons = document.querySelectorAll('#' + controlId + '-button-group > .tab-button');
107
127
  for (let i = 0; i < tabButtons.length; i++) {
108
128
  tabButtons[i].classList.remove('selected');
109
129
  }
110
- const tabContents = document.querySelectorAll('#' + controlId + '> .tab-content');
130
+ const tabContents = document.querySelectorAll('#' + controlId + '-content-group > .tab-content');
111
131
  for (let i = 0; i < tabContents.length; i++) {
112
- tabContents[i].style.display = 'none';
132
+ tabContents[i].classList.remove('selected')
113
133
  }
114
134
  const tabButton = event.currentTarget;
115
135
  tabButton.classList.add('selected');
116
- document.getElementById(tabId).style.display = "block";
136
+ document.getElementById(tabId).classList.add('selected');
117
137
  }
118
138
  """
119
139
  ).add_style(
120
140
  """
121
- .top .tab-button-group {
122
- overflow-x: hidden;
123
- border-bottom: 1px solid #EEE;
141
+ .tab-control {
142
+ border-spacing: 0px;
143
+ border-collapse: collapse;
144
+ height: 100%;
145
+ margin-top: 10px;
124
146
  }
125
- .left .tab-button-group {
126
- float: left;
127
- top: 0;
147
+ .tab-control td {
148
+ padding: 0px;
149
+ margin: 0px;
150
+ vertical-align: top;
151
+ }
152
+ .top.tab-button-group {
153
+ border-left: 1px solid #DDD;
154
+ border-top: 1px solid #DDD;
155
+ border-right: 1px solid #DDD;
156
+ border-radius: 5px 5px 0px 0px;
157
+ padding: 0px 5px 0px 0px;
158
+ margin-bottom: -2px;
128
159
  }
129
160
  .tab-button {
130
- background-color: #EEE;
131
- border: 1px solid #EEE;
161
+ background-color: transparent;
162
+ border-radius: 5px;
163
+ border: 0px;
164
+ font-weight: bold;
165
+ color: gray;
132
166
  outline: none;
133
167
  cursor: pointer;
134
168
  transition: 0.3s;
135
- padding: 10px 15px 10px 15px;
136
169
  }
137
- .top .tab-button {
138
- border-top-width: 2px;
170
+ .tab-button:hover {
171
+ background-color: #fff1dd;
139
172
  }
140
- .left .tab-button {
141
- display: block;
142
- width: 100%;
143
- border-left-width: 2px;
173
+ .tab-button.selected {
174
+ background-color: #f0ecf9;
175
+ color: black;
144
176
  }
145
- .top .tab-button.selected {
146
- border-bottom-color: #fff;
147
- border-top-color: #B721FF;
148
- background: #fff;
177
+ .top.tab-content-group {
178
+ border-left: 1px solid #DDD;
179
+ border-right: 1px solid #DDD;
180
+ border-bottom: 1px solid #DDD;
181
+ border-radius: 0px 0px 5px 5px;
182
+ margin: 0px;
183
+ padding: 5px;
184
+ height: 100%;
149
185
  }
150
- .left .tab-button.selected {
151
- border-right-color: #fff;
152
- border-left-color: #B721FF;
153
- background: #fff;
154
- }
155
- .top .tab-button:hover {
156
- border-top-color: orange;
157
- }
158
- .left .tab-button:hover {
159
- border-left-color: orange;
186
+ .top > .tab-button {
187
+ margin: 5px 0px 5px 5px;
160
188
  }
161
189
  .tab-content {
162
190
  display: none;
163
- padding: 10px;
164
191
  }
165
- .left .tab-content {
166
- float: left;
192
+ .tab-content.selected {
193
+ display: block;
194
+ }
195
+ .left.tab-button-group {
196
+ display: inline-flex;
197
+ flex-direction: column;
198
+ border: 1px solid #DDD;
199
+ border-radius: 5px;
200
+ margin-right: 5px;
201
+ padding: 0px 0px 5px 0px;
202
+ }
203
+ .left.tab-content-group {
204
+ border: 0px
205
+ margin: 0px;
206
+ padding: 0px;
207
+ height: 100%;
208
+ }
209
+ .left > .tab-button {
210
+ text-align: left;
211
+ margin: 5px 5px 0px 5px;
167
212
  }
168
213
  """
169
214
  )
215
+
216
+ def _tab_button(self, tab: Tab, i: int) -> Html:
217
+ return Html.element(
218
+ 'button',
219
+ [
220
+ tab.label
221
+ ],
222
+ css_classes=[
223
+ 'tab-button',
224
+ 'selected' if i == self.selected else None
225
+ ] + tab.css_classes,
226
+ onclick=(
227
+ f"""openTab(event, '{self.element_id()}', '{self.element_id(str(i))}')"""
228
+ )
229
+ )
230
+
231
+ def _tab_content(self, tab: Tab, i: int) -> Html:
232
+ return Html.element(
233
+ 'div',
234
+ [
235
+ tab.content
236
+ ],
237
+ css_classes=[
238
+ 'tab-content',
239
+ 'selected' if i == self.selected else None
240
+ ] + tab.css_classes,
241
+ id=self.element_id(str(i))
242
+ )
@@ -30,26 +30,24 @@ class TabControlTest(unittest.TestCase):
30
30
 
31
31
  def test_basic(self):
32
32
  tab = tab_lib.TabControl([
33
- tab_lib.Tab('foo', base.Html('<h1>foo</h1>')),
33
+ tab_lib.Tab('foo', base.Html('<h1>foo</h1>'), css_classes=['foo']),
34
34
  tab_lib.Tab('bar', base.Html('<h1>bar</h1>')),
35
35
  ])
36
+ elem_id = tab.element_id()
36
37
  self.assert_html_content(
37
38
  tab,
38
- (
39
- f'<div class="tab-control top" id="{tab.element_id()}">'
40
- '<div class="tab-button-group"><button class="tab-button selected" '
41
- f'''onclick="openTab(event, '{tab.element_id()}', '''
42
- f''''{tab.element_id(str(0))}')">'''
43
- '<a class="label">foo</a></button><button class="tab-button" '
44
- f'''onclick="openTab(event, '{tab.element_id()}', '''
45
- f''''{tab.element_id(str(1))}')">'''
46
- '<a class="label">bar</a></button></div>'
47
- '<div class="tab-content" style="display:block;" '
48
- f'id="{tab.element_id(str(0))}"><h1>foo</h1></div>'
49
- '<div class="tab-content" style="display:none;" '
50
- f'id="{tab.element_id(str(1))}"><h1>bar</h1></div></div>'
51
- )
39
+ f"""<table class="tab-control"><tr><td><div class="tab-button-group top" id="{elem_id}-button-group"><button class="tab-button selected foo" onclick="openTab(event, '{elem_id}', '{elem_id}-0')"><a class="label">foo</a></button><button class="tab-button" onclick="openTab(event, '{elem_id}', '{elem_id}-1')"><a class="label">bar</a></button></div></td></tr><tr><td><div class="tab-content-group top" id="{elem_id}-content-group"><div class="tab-content selected foo" id="{elem_id}-0"><h1>foo</h1></div><div class="tab-content" id="{elem_id}-1"><h1>bar</h1></div></div></td></tr></table>"""
52
40
  )
41
+ with tab.track_scripts() as scripts:
42
+ tab.extend([
43
+ tab_lib.Tab(
44
+ 'baz',
45
+ base.Html(
46
+ '<h1 class="a">baz</h1>').add_style('.a { color: red; }')
47
+ ),
48
+ tab_lib.Tab('qux', base.Html('<h1>qux</h1>')),
49
+ ])
50
+ self.assertEqual(len(scripts), 6)
53
51
 
54
52
 
55
53
  if __name__ == '__main__':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyglove
3
- Version: 0.4.5.dev202412080808
3
+ Version: 0.4.5.dev202412100810
4
4
  Summary: PyGlove: A library for manipulating Python objects.
5
5
  Home-page: https://github.com/google/pyglove
6
6
  Author: PyGlove Authors
@@ -143,18 +143,18 @@ pyglove/core/views/__init__.py,sha256=gll9ZBRYz4p_-LWOdzSR2a6UTWcJ8nR430trrP0yLC
143
143
  pyglove/core/views/base.py,sha256=Eq94AM5lryQ1IKuQsTSb7ZzX-lp2nhuOnS4ztMnCPIM,26447
144
144
  pyglove/core/views/base_test.py,sha256=UKbr_1TANOAnP7V5ICGF0UEkunfSaHiJ4nXZXhA0SaU,16642
145
145
  pyglove/core/views/html/__init__.py,sha256=Ff51MK1AKdzLM0kczTqLR3fRlJSJLDaFUdVyCu_7-eY,1085
146
- pyglove/core/views/html/base.py,sha256=PyS5B9N_Y2HS4q01X0uSUf3UbODn1UerfqPX1imLPac,15083
146
+ pyglove/core/views/html/base.py,sha256=S3dn2i9-yaH3wMUJRJFZDx72puhgDySyl1yFckiS6lM,15062
147
147
  pyglove/core/views/html/base_test.py,sha256=5RVPNm4YTamTFu6yXA1p2lJg3JHQBKNNBoL977qBZvE,22782
148
148
  pyglove/core/views/html/tree_view.py,sha256=3Db0aOke-A7Inp-vgqebsDMwJ0WvaznithkwcrfGbhg,52155
149
149
  pyglove/core/views/html/tree_view_test.py,sha256=whUorrw0eiDaZsEzGB2B3EN3wx0vLFuNEe2RBU03GeU,74707
150
150
  pyglove/core/views/html/controls/__init__.py,sha256=61qs5pnJPCTECCGBtkbNfIV3KcCu7cxfVNBEtIg1lMo,1318
151
- pyglove/core/views/html/controls/base.py,sha256=eTUB2iJSBtD0sbkWE_LaVT1e4Sir6Q61HJ2j5OZxAsU,6615
152
- pyglove/core/views/html/controls/label.py,sha256=JggkrD3j6Z_zAl2B8HRACC1pExDJUeIHY5E51yKMarw,5202
153
- pyglove/core/views/html/controls/label_test.py,sha256=3ekn3aBO1O8iG_wHpZ7487wY2YRA6wyKeDN7275iR4U,4544
151
+ pyglove/core/views/html/controls/base.py,sha256=VV-dDc_myKYlZeiJUXubIJXYKnm1ncEqjlqibN3d4tM,7710
152
+ pyglove/core/views/html/controls/label.py,sha256=JVLUByEHZ5uL8Mu65Y4UV33JIBo85Z7pE6Zcug6pOts,5644
153
+ pyglove/core/views/html/controls/label_test.py,sha256=4h6j4tZjAqhazp4AGnW1d_ohaeihofpggJ3UkK02Oho,5126
154
154
  pyglove/core/views/html/controls/progress_bar.py,sha256=kLdY3JQLCOv00ShldnQg3Qs8l1j_pDk148FoKfrZB64,5275
155
155
  pyglove/core/views/html/controls/progress_bar_test.py,sha256=6u1A6kAV8GY_vUMD6PSl7HuuOkVMELYqU32YEVx6qz8,3500
156
- pyglove/core/views/html/controls/tab.py,sha256=4m4pALSIQoIwVVz7hoMjQs1dgY3cOzyzJjT8ZikBNbQ,4958
157
- pyglove/core/views/html/controls/tab_test.py,sha256=j54nyN9BJjS0sXJlxA45RQDSmPY2HDP5ttxc4sqkGpw,2121
156
+ pyglove/core/views/html/controls/tab.py,sha256=HpHKprScIPtAgC4WbSk_ComU_TayC8q_aj54zZUc0ik,6858
157
+ pyglove/core/views/html/controls/tab_test.py,sha256=4tf8kiECcuY6vEcdgJWCY09UAfqMSwA2eJ-b-hxvLJw,2310
158
158
  pyglove/core/views/html/controls/tooltip.py,sha256=01BbpuM1twf3FYMUT09_Ck5JSSONe8QE9RmyA9nhCnU,3092
159
159
  pyglove/core/views/html/controls/tooltip_test.py,sha256=17BY-WmZKpz9tCbySPcwG6KJyfeE_MeMyKxtfxorBQ0,3194
160
160
  pyglove/ext/__init__.py,sha256=3jp8cJvKW6PENOZlmVAbT0w-GBRn_kjhc0wDX3XjpOE,755
@@ -196,8 +196,8 @@ pyglove/ext/scalars/randoms.py,sha256=LkMIIx7lOq_lvJvVS3BrgWGuWl7Pi91-lA-O8x_gZs
196
196
  pyglove/ext/scalars/randoms_test.py,sha256=nEhiqarg8l_5EOucp59CYrpO2uKxS1pe0hmBdZUzRNM,2000
197
197
  pyglove/ext/scalars/step_wise.py,sha256=IDw3tuTpv0KVh7AN44W43zqm1-E0HWPUlytWOQC9w3Y,3789
198
198
  pyglove/ext/scalars/step_wise_test.py,sha256=TL1vJ19xVx2t5HKuyIzGoogF7N3Rm8YhLE6JF7i0iy8,2540
199
- pyglove-0.4.5.dev202412080808.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
200
- pyglove-0.4.5.dev202412080808.dist-info/METADATA,sha256=dvUJIvfsqSKVKEPJ-5B-T_EtiVcf1qRHwA34i_UZpSQ,6666
201
- pyglove-0.4.5.dev202412080808.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
202
- pyglove-0.4.5.dev202412080808.dist-info/top_level.txt,sha256=wITzJSKcj8GZUkbq-MvUQnFadkiuAv_qv5qQMw0fIow,8
203
- pyglove-0.4.5.dev202412080808.dist-info/RECORD,,
199
+ pyglove-0.4.5.dev202412100810.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
200
+ pyglove-0.4.5.dev202412100810.dist-info/METADATA,sha256=DqoXAklEqKdWrQYxgzBOkjT_wpmSGfKzZse1unmazpk,6666
201
+ pyglove-0.4.5.dev202412100810.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
202
+ pyglove-0.4.5.dev202412100810.dist-info/top_level.txt,sha256=wITzJSKcj8GZUkbq-MvUQnFadkiuAv_qv5qQMw0fIow,8
203
+ pyglove-0.4.5.dev202412100810.dist-info/RECORD,,