gcf-python 2.2.0__py3-none-any.whl → 2.2.1__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.
gcf/__init__.py CHANGED
@@ -38,7 +38,7 @@ from .constants import KIND_ABBREV, KIND_EXPAND
38
38
  from .decode import DecodeError, decode
39
39
  from .delta import encode_delta
40
40
  from .encode import encode
41
- from .generic import encode_generic
41
+ from .generic import encode_generic, GenericOptions
42
42
  from .session import Session, encode_with_session
43
43
  from .decode_generic import decode_generic
44
44
  from .stream import StreamEncoder
@@ -62,6 +62,7 @@ __all__ = [
62
62
  "encode",
63
63
  "encode_delta",
64
64
  "encode_generic",
65
+ "GenericOptions",
65
66
  "encode_with_session",
66
67
  ]
67
68
 
gcf/decode_generic.py CHANGED
@@ -360,6 +360,14 @@ def _parse_attachment(
360
360
  arr, consumed = _parse_array_from_header(lines, line_idx, depth, after_name)
361
361
  return name, arr, consumed, None
362
362
 
363
+ # Scalar: =value (field names containing ">" excluded from tabular columns).
364
+ if after_name.startswith("="):
365
+ val_str = after_name[1:]
366
+ parsed = parse_scalar(val_str, tabular_context=True)
367
+ if parsed is MISSING:
368
+ return name, None, 1, None
369
+ return name, parsed, 1, None
370
+
363
371
  raise ValueError(f"invalid attachment form: {after_name}")
364
372
 
365
373
 
@@ -423,7 +431,11 @@ def _parse_tabular_body(
423
431
  path_column_map: dict[str, list[str]] = {}
424
432
  for f in fields:
425
433
  if ">" in f:
426
- path_column_map[f] = f.split(">")
434
+ parts = f.split(">")
435
+ # Only treat as a path column if all segments are non-empty.
436
+ # A literal key like ">" would split into ["", ""].
437
+ if all(p for p in parts):
438
+ path_column_map[f] = parts
427
439
 
428
440
  # Track inline schemas and shared array schemas.
429
441
  inline_schemas: dict[str, list[str]] = {}
@@ -506,10 +518,10 @@ def _parse_tabular_body(
506
518
  all_att_fields = traditional_att_fields + inline_att_fields
507
519
  attachment_values: dict[str, Any] = {}
508
520
 
509
- if row_has_id and all_att_fields:
521
+ if row_has_id:
510
522
  inline_idx = 0
511
523
 
512
- while i < len(lines) and len(attachment_values) < len(all_att_fields):
524
+ while i < len(lines):
513
525
  a_line = lines[i]
514
526
  a_content: str | None = None
515
527
  if depth == 0 or a_line.startswith(ind):
@@ -601,13 +613,6 @@ def _parse_tabular_body(
601
613
  if extra_name in attachment_values:
602
614
  raise ValueError(f"duplicate_attachment: {extra_name}")
603
615
 
604
- if not row_has_id or not all_att_fields:
605
- att_indent = ind + " "
606
- if i < len(lines) and lines[i].startswith(att_indent):
607
- peek = lines[i][len(att_indent):]
608
- if peek.startswith("."):
609
- raise ValueError(f"orphan_attachment: {peek}")
610
-
611
616
  row: dict[str, Any] = {}
612
617
  for f in fields:
613
618
  if f in missing_fields:
@@ -616,6 +621,10 @@ def _parse_tabular_body(
616
621
  row[f] = cell_values[f]
617
622
  elif f in attachment_values:
618
623
  row[f] = attachment_values[f]
624
+ # Also add any orphan attachment values (fields excluded from column list, e.g. ">" fields).
625
+ for k, v in attachment_values.items():
626
+ if k not in row:
627
+ row[k] = v
619
628
  # Unflatten path columns into nested objects.
620
629
  if path_column_map:
621
630
  nested = _unflatten_paths(path_column_map, flat_values, flat_absent)
gcf/generic.py CHANGED
@@ -2,42 +2,55 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from dataclasses import dataclass
5
6
  from typing import Any
6
7
 
7
8
  from .scalar import format_scalar, format_key
8
9
 
9
10
 
10
- def encode_generic(data: Any) -> str:
11
+ @dataclass
12
+ class GenericOptions:
13
+ """Options for controlling generic encoding behavior."""
14
+ no_flatten: bool = False
15
+ """When True, disables promotion of fixed-shape nested objects to path
16
+ columns (e.g. "customer>name"). Nested objects use attachment syntax
17
+ instead. Set when targeting open-weight models that show lower
18
+ comprehension on flattened encoding."""
19
+
20
+
21
+ def encode_generic(data: Any, opts: GenericOptions | None = None) -> str:
22
+ if opts is None:
23
+ opts = GenericOptions()
11
24
  out: list[str] = ["GCF profile=generic"]
12
- _encode_root_value(data, out)
25
+ _encode_root_value(data, out, opts)
13
26
  return "\n".join(out) + "\n"
14
27
 
15
28
 
16
- def _encode_root_value(v: Any, out: list[str]) -> None:
29
+ def _encode_root_value(v: Any, out: list[str], opts: GenericOptions) -> None:
17
30
  if v is None:
18
31
  out.append("=-")
19
32
  elif isinstance(v, dict):
20
- _encode_object(v, out, 0)
33
+ _encode_object(v, out, 0, opts)
21
34
  elif isinstance(v, list):
22
- _encode_root_array(v, out)
35
+ _encode_root_array(v, out, opts)
23
36
  else:
24
37
  out.append(f"={format_scalar(v)}")
25
38
 
26
39
 
27
- def _encode_object(d: dict, out: list[str], depth: int) -> None:
40
+ def _encode_object(d: dict, out: list[str], depth: int, opts: GenericOptions) -> None:
28
41
  prefix = _indent(depth)
29
42
  for key, value in d.items():
30
43
  fk = format_key(key)
31
44
  if isinstance(value, dict):
32
45
  out.append(f"{prefix}## {fk}")
33
- _encode_object(value, out, depth + 1)
46
+ _encode_object(value, out, depth + 1, opts)
34
47
  elif isinstance(value, list):
35
- _encode_named_array(fk, value, out, depth)
48
+ _encode_named_array(fk, value, out, depth, opts)
36
49
  else:
37
50
  out.append(f"{prefix}{fk}={format_scalar(value)}")
38
51
 
39
52
 
40
- def _encode_root_array(arr: list, out: list[str]) -> None:
53
+ def _encode_root_array(arr: list, out: list[str], opts: GenericOptions) -> None:
41
54
  if not arr:
42
55
  out.append("## [0]")
43
56
  return
@@ -47,12 +60,12 @@ def _encode_root_array(arr: list, out: list[str]) -> None:
47
60
  return
48
61
  fields = _tabular_fields(arr)
49
62
  if fields is not None:
50
- _encode_tabular("## ", arr, fields, out, 0)
63
+ _encode_tabular("## ", arr, fields, out, 0, opts)
51
64
  return
52
- _encode_expanded("## ", arr, out, 0)
65
+ _encode_expanded("## ", arr, out, 0, opts)
53
66
 
54
67
 
55
- def _encode_named_array(name: str, arr: list, out: list[str], depth: int) -> None:
68
+ def _encode_named_array(name: str, arr: list, out: list[str], depth: int, opts: GenericOptions) -> None:
56
69
  prefix = _indent(depth)
57
70
  if not arr:
58
71
  out.append(f"{prefix}## {name} [0]")
@@ -63,9 +76,9 @@ def _encode_named_array(name: str, arr: list, out: list[str], depth: int) -> Non
63
76
  return
64
77
  fields = _tabular_fields(arr)
65
78
  if fields is not None:
66
- _encode_tabular(f"{prefix}## {name} ", arr, fields, out, depth)
79
+ _encode_tabular(f"{prefix}## {name} ", arr, fields, out, depth, opts)
67
80
  return
68
- _encode_expanded(f"{prefix}## {name} ", arr, out, depth)
81
+ _encode_expanded(f"{prefix}## {name} ", arr, out, depth, opts)
69
82
 
70
83
 
71
84
  def _tabular_fields(arr: list) -> list[str] | None:
@@ -152,6 +165,9 @@ def _analyze_flattenable(
152
165
  arr: list[dict], field_name: str, parent_path: str
153
166
  ) -> list[dict] | None:
154
167
  """Analyze whether a field can be flattened. Returns list of leaf descriptors or None."""
168
+ # Field names containing ">" cannot be flattened (would create ambiguous paths).
169
+ if ">" in field_name:
170
+ return None
155
171
  canonical_shape: dict[str, str] | None = None # key -> "scalar" | "nested"
156
172
 
157
173
  for item in arr:
@@ -249,26 +265,39 @@ def _resolve_key_chain(item: Any, keys: list[str]) -> tuple[Any, bool]:
249
265
 
250
266
 
251
267
  def _encode_tabular(
252
- header_prefix: str, arr: list[dict], fields: list[str], out: list[str], depth: int
268
+ header_prefix: str, arr: list[dict], fields: list[str], out: list[str], depth: int, opts: GenericOptions
253
269
  ) -> None:
254
270
  prefix = _indent(depth)
255
271
 
256
272
  # Phase 0: Analyze fields for flattening.
257
273
  flatten_map: dict[str, list[dict]] = {}
258
- for f in fields:
259
- leaves = _analyze_flattenable(arr, f, "")
260
- if leaves and len(leaves) > 0:
261
- flatten_map[f] = leaves
274
+ if not opts.no_flatten:
275
+ for f in fields:
276
+ leaves = _analyze_flattenable(arr, f, "")
277
+ if leaves and len(leaves) > 0:
278
+ flatten_map[f] = leaves
279
+
280
+ # Fields whose names contain ">" must not appear as tabular columns
281
+ # because the decoder would interpret them as flattened path columns.
282
+ # Track them for per-row attachment emission (spec rule 7.4.6.1.4).
283
+ gt_fields = {f for f in fields if f not in flatten_map and ">" in f}
262
284
 
263
285
  # Build expanded column list.
264
286
  columns: list[dict] = []
265
287
  for f in fields:
288
+ if f in gt_fields:
289
+ continue
266
290
  if f in flatten_map:
267
291
  for leaf in flatten_map[f]:
268
292
  columns.append({"header": format_key(leaf["path"]), "type": "flat", "field": f, "keys": leaf["keys"]})
269
293
  else:
270
294
  columns.append({"header": format_key(f), "type": "original", "field": f, "keys": []})
271
295
 
296
+ # If all fields were excluded (all contain ">"), fall back to expanded.
297
+ if not columns:
298
+ _encode_expanded(header_prefix, arr, out, depth, opts)
299
+ return
300
+
272
301
  # Pre-compute inline schemas and shared array schemas (skip flattened fields).
273
302
  inline_schemas: dict[str, list[str]] = {}
274
303
  shared_arr_schemas: dict[str, list[str]] = {}
@@ -333,6 +362,15 @@ def _encode_tabular(
333
362
  else:
334
363
  cells.append(format_scalar(v, "|"))
335
364
 
365
+ # Emit fields with ">" in their names as per-row attachments.
366
+ for f in fields:
367
+ if f not in gt_fields:
368
+ continue
369
+ if f not in item:
370
+ continue
371
+ row_has_attachment = True
372
+ attachments.append((f, item[f], False, None))
373
+
336
374
  row = "|".join(cells)
337
375
  if row_has_attachment:
338
376
  out.append(f"{prefix}@{i} {row}")
@@ -351,17 +389,25 @@ def _encode_tabular(
351
389
  elif isinstance(att_val, list):
352
390
  sas = shared_arr_schemas.get(att_name)
353
391
  if sas and i > 0:
354
- _encode_attachment_array_shared(prefix, fk, att_val, out, depth + 2, sas)
392
+ _encode_attachment_array_shared(prefix, fk, att_val, out, depth + 2, sas, opts)
355
393
  else:
356
- _encode_attachment_array(prefix, fk, att_val, out, depth + 2)
394
+ _encode_attachment_array(prefix, fk, att_val, out, depth + 2, opts)
357
395
  elif isinstance(att_val, dict):
358
396
  out.append(f"{prefix}.{fk} {{}}")
359
- _encode_object(att_val, out, depth + 2)
397
+ _encode_object(att_val, out, depth + 2, opts)
398
+ else:
399
+ # Scalar attachment (e.g. field names containing ">").
400
+ if att_val is None:
401
+ out.append(f"{prefix}.{fk} =-")
402
+ else:
403
+ out.append(f"{prefix}.{fk} ={format_scalar(att_val)}")
360
404
 
361
405
 
362
406
  def _encode_attachment_array(
363
- att_prefix: str, fk: str, arr: list, out: list[str], depth: int
407
+ att_prefix: str, fk: str, arr: list, out: list[str], depth: int, opts: GenericOptions | None = None
364
408
  ) -> None:
409
+ if opts is None:
410
+ opts = GenericOptions()
365
411
  if not arr:
366
412
  out.append(f"{att_prefix}.{fk} [0]")
367
413
  elif _all_primitives(arr):
@@ -370,13 +416,13 @@ def _encode_attachment_array(
370
416
  else:
371
417
  fields = _tabular_fields(arr)
372
418
  if fields is not None:
373
- _encode_tabular(f"{att_prefix}.{fk} ", arr, fields, out, depth)
419
+ _encode_tabular(f"{att_prefix}.{fk} ", arr, fields, out, depth, opts)
374
420
  else:
375
- _encode_expanded(f"{att_prefix}.{fk} ", arr, out, depth)
421
+ _encode_expanded(f"{att_prefix}.{fk} ", arr, out, depth, opts)
376
422
 
377
423
 
378
424
  def _encode_attachment_array_shared(
379
- att_prefix: str, fk: str, arr: list, out: list[str], depth: int, shared_fields: list[str]
425
+ att_prefix: str, fk: str, arr: list, out: list[str], depth: int, shared_fields: list[str], opts: GenericOptions | None = None
380
426
  ) -> None:
381
427
  if not arr:
382
428
  out.append(f"{att_prefix}.{fk} [0]")
@@ -403,25 +449,29 @@ def _encode_attachment_array_shared(
403
449
  out.append(f"{prefix}{'|'.join(cells)}")
404
450
  else:
405
451
  # Fields don't match: fall back to full encoding.
406
- _encode_attachment_array(att_prefix, fk, arr, out, depth)
452
+ _encode_attachment_array(att_prefix, fk, arr, out, depth, opts)
407
453
 
408
454
 
409
- def _encode_expanded(header_prefix: str, arr: list, out: list[str], depth: int) -> None:
455
+ def _encode_expanded(header_prefix: str, arr: list, out: list[str], depth: int, opts: GenericOptions | None = None) -> None:
456
+ if opts is None:
457
+ opts = GenericOptions()
410
458
  prefix = _indent(depth)
411
459
  out.append(f"{header_prefix}[{len(arr)}]")
412
460
  for i, item in enumerate(arr):
413
461
  if isinstance(item, dict):
414
462
  out.append(f"{prefix}@{i} {{}}")
415
- _encode_object(item, out, depth + 1)
463
+ _encode_object(item, out, depth + 1, opts)
416
464
  elif isinstance(item, list):
417
- _encode_expanded_array_item(prefix, i, item, out, depth)
465
+ _encode_expanded_array_item(prefix, i, item, out, depth, opts)
418
466
  else:
419
467
  out.append(f"{prefix}@{i} ={format_scalar(item)}")
420
468
 
421
469
 
422
470
  def _encode_expanded_array_item(
423
- prefix: str, idx: int, arr: list, out: list[str], depth: int
471
+ prefix: str, idx: int, arr: list, out: list[str], depth: int, opts: GenericOptions | None = None
424
472
  ) -> None:
473
+ if opts is None:
474
+ opts = GenericOptions()
425
475
  if not arr:
426
476
  out.append(f"{prefix}@{idx} [0]")
427
477
  elif _all_primitives(arr):
@@ -430,9 +480,9 @@ def _encode_expanded_array_item(
430
480
  else:
431
481
  fields = _tabular_fields(arr)
432
482
  if fields is not None:
433
- _encode_tabular(f"{prefix}@{idx} ", arr, fields, out, depth + 1)
483
+ _encode_tabular(f"{prefix}@{idx} ", arr, fields, out, depth + 1, opts)
434
484
  else:
435
- _encode_expanded(f"{prefix}@{idx} ", arr, out, depth + 1)
485
+ _encode_expanded(f"{prefix}@{idx} ", arr, out, depth + 1, opts)
436
486
 
437
487
 
438
488
  def _all_primitives(arr: list) -> bool:
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gcf-python
3
- Version: 2.2.0
4
- Summary: Drop-in JSON replacement for AI pipelines. 79% fewer tokens. 90.7% comprehension across 10 models. Zero dependencies.
3
+ Version: 2.2.1
4
+ Summary: The AI-native wire format for structured data. 50-92% fewer tokens than JSON. 100% comprehension on every frontier model. Zero dependencies.
5
5
  Project-URL: Homepage, https://github.com/blackwell-systems/gcf-python
6
6
  Project-URL: Documentation, https://blackwell-systems.github.io/gcf/
7
7
  Project-URL: Specification, https://github.com/blackwell-systems/gcf
@@ -32,7 +32,7 @@ Description-Content-Type: text/markdown
32
32
 
33
33
  Python implementation of [GCF](https://gcformat.com/) — the most token-efficient wire format for LLMs. A drop-in alternative to JSON and TOON for any structured data.
34
34
 
35
- **100% comprehension on every frontier model tested. 25.5% fewer tokens than TOON, 53% fewer than JSON across 15 datasets. 90.7% on structurally complex code graphs (vs TOON 68.5%, JSON 53.6%). 1,700+ LLM evaluations. Zero training.**
35
+ **100% comprehension on every frontier model tested. 29% fewer tokens than TOON, 56% fewer than JSON across 16 datasets. 91.2% on structurally complex code graphs (vs TOON 68.2%, JSON 53.4%). 2,400+ LLM evaluations. Zero training.**
36
36
 
37
37
  Docs: [gcformat.com](https://gcformat.com/) · [Playground](https://gcformat.com/playground.html) · [GCF vs TOON](https://gcformat.com/guide/vs-toon.html)
38
38
 
@@ -206,16 +206,16 @@ Works on dicts, lists, and primitives. Lists of uniform dicts get tabular rows.
206
206
 
207
207
  ## Benchmarks
208
208
 
209
- 1,700+ LLM evaluations across 10 models, 3 providers, and 51 independent test runs.
209
+ 2,400+ LLM evaluations across 10 models, 3 providers, and 51 independent test runs.
210
210
 
211
211
  | | GCF | TOON | JSON |
212
212
  |---|---|---|---|
213
- | **Comprehension** (23 runs, 10 models) | **90.7%** | 68.5% | 53.6% |
213
+ | **Comprehension** (23 runs, 10 models) | **91.2%** | 68.2% | 53.4% |
214
214
  | **Generation** (28 runs, 9 models) | **5/5** | 1.0/5 | 5.0/5 |
215
215
  | **Input tokens** (500 symbols) | **11,090** | 16,378 | 53,341 |
216
216
  | **Output tokens** (100 symbols) | **5,976** | 8,937 | 16,121 |
217
217
 
218
- GCF wins 13/15 datasets on the expanded [token efficiency benchmark](https://github.com/blackwell-systems/toon/tree/gcf-comparison). Full results: [gcformat.com/guide/benchmarks](https://gcformat.com/guide/benchmarks.html)
218
+ GCF wins 15/16 datasets on the expanded [token efficiency benchmark](https://github.com/blackwell-systems/toon/tree/gcf-comparison). Full results: [gcformat.com/guide/benchmarks](https://gcformat.com/guide/benchmarks.html)
219
219
 
220
220
  ## Implementations
221
221
 
@@ -236,7 +236,7 @@ GCF wins 13/15 datasets on the expanded [token efficiency benchmark](https://git
236
236
 
237
237
  **Zero runtime dependencies. Permanently.** All six implementations depend only on their language's standard library. No transitive dependencies. No supply chain risk. This is a permanent commitment: GCF will never take on external runtime dependencies. MIT licensed. All implementations support both generic profile (`encodeGeneric`) and graph profile (`encode`). CLI included in all 6 languages.
238
238
 
239
- **Specification:** [SPEC v3.1 Stable](https://github.com/blackwell-systems/gcf/blob/main/SPEC.md) with 157 conformance fixtures, 33,000,000,000+ lossless round-trips verified across 5 formats and 6 languages. All implementations at v2.1.0+ (Go v1.2.0). Cross-language 6x6 matrix verified.
239
+ **Specification:** [SPEC v3.2 Stable](https://github.com/blackwell-systems/gcf/blob/main/SPEC.md) with 173 conformance fixtures, 43,000,000,000+ lossless round-trips verified across 5 formats and 6 languages. All implementations at v2.2.0+ (Go v1.3.0). Cross-language 6x6 matrix verified.
240
240
 
241
241
  ## License
242
242
 
@@ -1,19 +1,19 @@
1
- gcf/__init__.py,sha256=UBJ5UmpO1oG8bCZnO1jlPngUL65Gg1yL9sfArC9ed2g,1738
1
+ gcf/__init__.py,sha256=AlIvWioau3i54BViE2rZsNuXCpQKD9tzwA7_jOKq2u4,1776
2
2
  gcf/__main__.py,sha256=EpvBz1yc8H0D5OJ1zy2tYke-kRzvudKa4DEbfeW14ao,71
3
3
  gcf/cli.py,sha256=UEe1CAZn-rKGNIo_ap8-oez3ucl6DSRbsdv6RDnzygY,5256
4
4
  gcf/constants.py,sha256=cmZ8YJSOB0im_eyfN8v4UvrLpBC6Fuf4cfcKZGbutxY,638
5
5
  gcf/decode.py,sha256=nD8bXYhoeHQQ3LCeAJQOAgFuob-V_6us4mcBYtL_bBc,5978
6
- gcf/decode_generic.py,sha256=QdwXIPZhTTJsfaYInYKW0TKr8Ln5cufmRE2kl40_kpY,26034
6
+ gcf/decode_generic.py,sha256=ZlFZqwenOzCnvbJijKscb6aQHoHQ81qoHBkaalVbqqo,26381
7
7
  gcf/delta.py,sha256=f90UC6zejXH-ujyU_OViWDCqnxLXk3i6Qion-RJGHY4,1670
8
8
  gcf/encode.py,sha256=CcqMSNmojrQAAw2X3MNfw8YR6l4rNw9hWDqwmf846oA,2929
9
- gcf/generic.py,sha256=7cSwYT_U3ySTZy8yLY_GtQrzzGIRkhHOE1SEIGmhdrg,15458
9
+ gcf/generic.py,sha256=I06-vcVtcCg38X6uaoTMy7ipWiIFLaBMgQpTtuGvg2o,17763
10
10
  gcf/scalar.py,sha256=MZay-KIROvaFHet-g2-pBghahT1bf_5bxZjG4yTZkSo,9329
11
11
  gcf/session.py,sha256=6-wytNGaBkgqH7uTar7ojUFYNwIMeEVAZhuV-s-upZM,4704
12
12
  gcf/stream.py,sha256=1Kt_a2daKpYHlWP1lnDJ4g549pUt9rc-U0xbARbXRKQ,5174
13
13
  gcf/stream_generic.py,sha256=RnqqiPSu5joJa-7e58QbbzSGfvxBICA587A3aArjZvE,3250
14
14
  gcf/types.py,sha256=AWm-LQoSqLHAYtEjcAxWQZqJ4JXqNreLUKO2mJFgNMA,1465
15
- gcf_python-2.2.0.dist-info/METADATA,sha256=Kc-fEX3jOtbvgBPwEiQL0gRf4hibJYlGJu4ea05kGfw,9781
16
- gcf_python-2.2.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
17
- gcf_python-2.2.0.dist-info/entry_points.txt,sha256=aFT6gqlkh8iGfM8cblE-LUMxHH08_v71IIoZtDdRIVA,37
18
- gcf_python-2.2.0.dist-info/licenses/LICENSE,sha256=2Fit9wnaIe--RMSAgyQqxC5hfZTyZqn4fIdBtp9qPDw,1072
19
- gcf_python-2.2.0.dist-info/RECORD,,
15
+ gcf_python-2.2.1.dist-info/METADATA,sha256=izqgkRxKLUVqw5_sZqcs50i9kMAP3NRZnY8EuBDLVt8,9802
16
+ gcf_python-2.2.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
17
+ gcf_python-2.2.1.dist-info/entry_points.txt,sha256=aFT6gqlkh8iGfM8cblE-LUMxHH08_v71IIoZtDdRIVA,37
18
+ gcf_python-2.2.1.dist-info/licenses/LICENSE,sha256=2Fit9wnaIe--RMSAgyQqxC5hfZTyZqn4fIdBtp9qPDw,1072
19
+ gcf_python-2.2.1.dist-info/RECORD,,