tnfr 4.1.0__py3-none-any.whl → 4.5.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.

Potentially problematic release.


This version of tnfr might be problematic. Click here for more details.

tnfr/sense.py CHANGED
@@ -4,7 +4,7 @@ import math
4
4
  from collections import Counter
5
5
 
6
6
  from .constants import DEFAULTS, ALIAS_SI, ALIAS_EPI
7
- from .helpers import _get_attr, clamp01, register_callback
7
+ from .helpers import _get_attr, clamp01, register_callback, ensure_history, last_glifo
8
8
 
9
9
  # -------------------------
10
10
  # Canon: orden circular de glifos y ángulos
@@ -60,23 +60,14 @@ def _weight(G, n, mode: str) -> float:
60
60
  return 1.0
61
61
 
62
62
 
63
- def _last_glifo(nd: Dict[str, Any]) -> str | None:
64
- hist = nd.get("hist_glifos")
65
- if not hist:
66
- return None
67
- try:
68
- return list(hist)[-1]
69
- except Exception:
70
- return None
71
-
72
-
63
+
73
64
  # -------------------------
74
65
  # σ por nodo y σ global
75
66
  # -------------------------
76
67
 
77
68
  def sigma_vector_node(G, n, weight_mode: str | None = None) -> Dict[str, float] | None:
78
69
  nd = G.nodes[n]
79
- g = _last_glifo(nd)
70
+ g = last_glifo(nd)
80
71
  if g is None:
81
72
  return None
82
73
  w = _weight(G, n, weight_mode or G.graph.get("SIGMA", DEFAULTS["SIGMA"]).get("weight", "Si"))
@@ -120,17 +111,11 @@ def sigma_vector_global(G, weight_mode: str | None = None) -> Dict[str, float]:
120
111
  # Historia / series
121
112
  # -------------------------
122
113
 
123
- def _ensure_history(G):
124
- if "history" not in G.graph:
125
- G.graph["history"] = {}
126
- return G.graph["history"]
127
-
128
-
129
114
  def push_sigma_snapshot(G, t: float | None = None) -> None:
130
115
  cfg = G.graph.get("SIGMA", DEFAULTS["SIGMA"])
131
116
  if not cfg.get("enabled", True):
132
117
  return
133
- hist = _ensure_history(G)
118
+ hist = ensure_history(G)
134
119
  key = cfg.get("history_key", "sigma_global")
135
120
 
136
121
  # Global
@@ -153,7 +138,7 @@ def push_sigma_snapshot(G, t: float | None = None) -> None:
153
138
  # Conteo de glifos por paso (útil para rosa glífica)
154
139
  counts = Counter()
155
140
  for n in G.nodes():
156
- g = _last_glifo(G.nodes[n])
141
+ g = last_glifo(G.nodes[n])
157
142
  if g:
158
143
  counts[g] += 1
159
144
  hist.setdefault("sigma_counts", []).append({"t": sv["t"], **counts})
@@ -163,7 +148,7 @@ def push_sigma_snapshot(G, t: float | None = None) -> None:
163
148
  per = hist.setdefault("sigma_per_node", {})
164
149
  for n in G.nodes():
165
150
  nd = G.nodes[n]
166
- g = _last_glifo(nd)
151
+ g = last_glifo(nd)
167
152
  if not g:
168
153
  continue
169
154
  a = glyph_angle(g)
tnfr/structural.py ADDED
@@ -0,0 +1,201 @@
1
+ from __future__ import annotations
2
+ """API de operadores estructurales y secuencias TNFR.
3
+
4
+ Este módulo ofrece:
5
+ - Factoría `create_nfr` para inicializar redes/nodos TNFR.
6
+ - Clases de operador (`Operador` y derivados) con interfaz común.
7
+ - Registro de operadores `OPERADORES`.
8
+ - Utilidades `validate_sequence` y `run_sequence` para ejecutar
9
+ secuencias canónicas de operadores.
10
+ """
11
+ from typing import Iterable, Tuple, List
12
+ import networkx as nx
13
+
14
+ from .dynamics import (
15
+ set_delta_nfr_hook,
16
+ update_epi_via_nodal_equation,
17
+ dnfr_epi_vf_mixed,
18
+ )
19
+ from .operators import aplicar_glifo
20
+ from .constants import ALIAS_EPI, ALIAS_VF, ALIAS_THETA
21
+
22
+
23
+ # ---------------------------------------------------------------------------
24
+ # 1) Factoría NFR
25
+ # ---------------------------------------------------------------------------
26
+
27
+ def create_nfr(
28
+ name: str,
29
+ *,
30
+ epi: float = 0.0,
31
+ vf: float = 1.0,
32
+ theta: float = 0.0,
33
+ graph: nx.Graph | None = None,
34
+ dnfr_hook=dnfr_epi_vf_mixed,
35
+ ) -> Tuple[nx.Graph, str]:
36
+ """Crea una red (graph) con un nodo NFR inicializado.
37
+
38
+ Devuelve la tupla ``(G, name)`` para conveniencia.
39
+ """
40
+ G = graph or nx.Graph()
41
+ G.add_node(
42
+ name,
43
+ **{
44
+ ALIAS_EPI[0]: float(epi),
45
+ ALIAS_VF[0]: float(vf),
46
+ ALIAS_THETA[0]: float(theta),
47
+ }
48
+ )
49
+ set_delta_nfr_hook(G, dnfr_hook)
50
+ return G, name
51
+
52
+
53
+ # ---------------------------------------------------------------------------
54
+ # 2) Operadores estructurales como API de primer orden
55
+ # ---------------------------------------------------------------------------
56
+
57
+
58
+ class Operador:
59
+ """Base para operadores TNFR.
60
+
61
+ Cada operador define ``name`` (identificador ASCII) y ``glyph`` (glifo
62
+ canónico). La llamada ejecuta el glifo correspondiente sobre el nodo.
63
+ """
64
+
65
+ name = "operador"
66
+ glyph = None # tipo: str
67
+
68
+ def __call__(self, G: nx.Graph, node, **kw) -> None:
69
+ if self.glyph is None:
70
+ raise NotImplementedError("Operador sin glifo asignado")
71
+ aplicar_glifo(G, node, self.glyph, **kw)
72
+
73
+
74
+ # Derivados concretos -------------------------------------------------------
75
+ class Emision(Operador):
76
+ name = "emision"
77
+ glyph = "A’L"
78
+
79
+
80
+ class Recepcion(Operador):
81
+ name = "recepcion"
82
+ glyph = "E’N"
83
+
84
+
85
+ class Coherencia(Operador):
86
+ name = "coherencia"
87
+ glyph = "I’L"
88
+
89
+
90
+ class Disonancia(Operador):
91
+ name = "disonancia"
92
+ glyph = "O’Z"
93
+
94
+
95
+ class Acoplamiento(Operador):
96
+ name = "acoplamiento"
97
+ glyph = "U’M"
98
+
99
+
100
+ class Resonancia(Operador):
101
+ name = "resonancia"
102
+ glyph = "R’A"
103
+
104
+
105
+ class Silencio(Operador):
106
+ name = "silencio"
107
+ glyph = "SH’A"
108
+
109
+
110
+ class Expansion(Operador):
111
+ name = "expansion"
112
+ glyph = "VA’L"
113
+
114
+
115
+ class Contraccion(Operador):
116
+ name = "contraccion"
117
+ glyph = "NU’L"
118
+
119
+
120
+ class Autoorganizacion(Operador):
121
+ name = "autoorganizacion"
122
+ glyph = "T’HOL"
123
+
124
+
125
+ class Mutacion(Operador):
126
+ name = "mutacion"
127
+ glyph = "Z’HIR"
128
+
129
+
130
+ class Transicion(Operador):
131
+ name = "transicion"
132
+ glyph = "NA’V"
133
+
134
+
135
+ class Recursividad(Operador):
136
+ name = "recursividad"
137
+ glyph = "RE’MESH"
138
+
139
+
140
+ OPERADORES = {
141
+ op().name: op
142
+ for op in [
143
+ Emision,
144
+ Recepcion,
145
+ Coherencia,
146
+ Disonancia,
147
+ Acoplamiento,
148
+ Resonancia,
149
+ Silencio,
150
+ Expansion,
151
+ Contraccion,
152
+ Autoorganizacion,
153
+ Mutacion,
154
+ Transicion,
155
+ Recursividad,
156
+ ]
157
+ }
158
+
159
+
160
+ # ---------------------------------------------------------------------------
161
+ # 3) Motor de secuencias + validador sintáctico
162
+ # ---------------------------------------------------------------------------
163
+
164
+
165
+ _INICIO_VALIDOS = {"emision", "recursividad"}
166
+ _TRAMO_INTERMEDIO = {"disonancia", "acoplamiento", "resonancia"}
167
+ _CIERRE_VALIDO = {"silencio", "transicion", "recursividad"}
168
+
169
+
170
+ def validate_sequence(nombres: List[str]) -> Tuple[bool, str]:
171
+ """Valida reglas mínimas de la sintaxis TNFR."""
172
+ if not nombres:
173
+ return False, "secuencia vacía"
174
+ if nombres[0] not in _INICIO_VALIDOS:
175
+ return False, "debe iniciar en emisión o recursividad"
176
+ try:
177
+ i_rec = nombres.index("recepcion")
178
+ i_coh = nombres.index("coherencia", i_rec + 1)
179
+ except ValueError:
180
+ return False, "falta tramo entrada→coherencia"
181
+ if not any(n in _TRAMO_INTERMEDIO for n in nombres[i_coh + 1 :]):
182
+ return False, "falta tramo de tensión/acoplamiento/resonancia"
183
+ if not any(n in _CIERRE_VALIDO for n in nombres[-2:]):
184
+ return False, "falta cierre (silencio/transición/recursividad)"
185
+ return True, "ok"
186
+
187
+
188
+ def run_sequence(G: nx.Graph, node, ops: Iterable[Operador]) -> None:
189
+ """Ejecuta una secuencia validada de operadores sobre el nodo dado."""
190
+ ops_list = list(ops)
191
+ nombres = [op.name for op in ops_list]
192
+ ok, msg = validate_sequence(nombres)
193
+ if not ok:
194
+ raise ValueError(f"Secuencia no válida: {msg}")
195
+ for op in ops_list:
196
+ op(G, node)
197
+ compute = G.graph.get("compute_delta_nfr")
198
+ if callable(compute):
199
+ compute(G)
200
+ update_epi_via_nodal_equation(G)
201
+
tnfr/trace.py CHANGED
@@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional
3
3
  from collections import Counter
4
4
 
5
5
  from .constants import DEFAULTS
6
- from .helpers import register_callback
6
+ from .helpers import register_callback, ensure_history, last_glifo
7
7
 
8
8
  try:
9
9
  from .gamma import kuramoto_R_psi
@@ -30,22 +30,6 @@ DEFAULTS.setdefault("TRACE", {
30
30
  # Helpers
31
31
  # -------------------------
32
32
 
33
- def _ensure_history(G):
34
- if "history" not in G.graph:
35
- G.graph["history"] = {}
36
- return G.graph["history"]
37
-
38
-
39
- def _last_glifo(nd: Dict[str, Any]) -> str | None:
40
- h = nd.get("hist_glifos")
41
- if not h:
42
- return None
43
- try:
44
- return list(h)[-1]
45
- except Exception:
46
- return None
47
-
48
-
49
33
  # -------------------------
50
34
  # Snapshots
51
35
  # -------------------------
@@ -55,7 +39,7 @@ def _trace_before(G, *args, **kwargs):
55
39
  return
56
40
  cfg = G.graph.get("TRACE", DEFAULTS["TRACE"])
57
41
  capture: List[str] = list(cfg.get("capture", []))
58
- hist = _ensure_history(G)
42
+ hist = ensure_history(G)
59
43
  key = cfg.get("history_key", "trace_meta")
60
44
 
61
45
  meta: Dict[str, Any] = {"t": float(G.graph.get("_t", 0.0)), "phase": "before"}
@@ -103,7 +87,7 @@ def _trace_after(G, *args, **kwargs):
103
87
  return
104
88
  cfg = G.graph.get("TRACE", DEFAULTS["TRACE"])
105
89
  capture: List[str] = list(cfg.get("capture", []))
106
- hist = _ensure_history(G)
90
+ hist = ensure_history(G)
107
91
  key = cfg.get("history_key", "trace_meta")
108
92
 
109
93
  meta: Dict[str, Any] = {"t": float(G.graph.get("_t", 0.0)), "phase": "after"}
@@ -119,7 +103,7 @@ def _trace_after(G, *args, **kwargs):
119
103
  if "glifo_counts" in capture:
120
104
  cnt = Counter()
121
105
  for n in G.nodes():
122
- g = _last_glifo(G.nodes[n])
106
+ g = last_glifo(G.nodes[n])
123
107
  if g:
124
108
  cnt[g] += 1
125
109
  meta["glifos"] = dict(cnt)
tnfr/types.py CHANGED
@@ -9,9 +9,10 @@ class NodeState:
9
9
  vf: float = 1.0 # νf
10
10
  theta: float = 0.0 # θ
11
11
  Si: float = 0.5
12
+ epi_kind: str = ""
12
13
  extra: Dict[str, Any] = field(default_factory=dict)
13
14
 
14
15
  def to_attrs(self) -> Dict[str, Any]:
15
- d = {"EPI": self.EPI, "νf": self.vf, "θ": self.theta, "Si": self.Si}
16
+ d = {"EPI": self.EPI, "νf": self.vf, "θ": self.theta, "Si": self.Si, "EPI_kind": self.epi_kind}
16
17
  d.update(self.extra)
17
18
  return d
tnfr/validators.py ADDED
@@ -0,0 +1,38 @@
1
+ """Validadores de invariantes TNFR."""
2
+
3
+ from __future__ import annotations
4
+ from typing import Iterable
5
+
6
+ from .constants import ALIAS_EPI, DEFAULTS
7
+ from .helpers import _get_attr
8
+ from .sense import sigma_vector_global, GLYPHS_CANONICAL
9
+ from .helpers import last_glifo
10
+
11
+
12
+ def _validate_epi(G) -> None:
13
+ emin = float(G.graph.get("EPI_MIN", DEFAULTS.get("EPI_MIN", -1.0)))
14
+ emax = float(G.graph.get("EPI_MAX", DEFAULTS.get("EPI_MAX", 1.0)))
15
+ for n in G.nodes():
16
+ x = float(_get_attr(G.nodes[n], ALIAS_EPI, 0.0))
17
+ if not (emin - 1e-9 <= x <= emax + 1e-9):
18
+ raise ValueError(f"EPI fuera de rango en nodo {n}: {x}")
19
+
20
+
21
+ def _validate_sigma(G) -> None:
22
+ sv = sigma_vector_global(G)
23
+ if sv.get("mag", 0.0) > 1.0 + 1e-9:
24
+ raise ValueError("Norma de σ excede 1")
25
+
26
+
27
+ def _validate_glifos(G) -> None:
28
+ for n in G.nodes():
29
+ g = last_glifo(G.nodes[n])
30
+ if g and g not in GLYPHS_CANONICAL:
31
+ raise ValueError(f"Glifo inválido {g} en nodo {n}")
32
+
33
+
34
+ def run_validators(G) -> None:
35
+ """Ejecuta todos los validadores de invariantes sobre ``G``."""
36
+ _validate_epi(G)
37
+ _validate_sigma(G)
38
+ _validate_glifos(G)
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tnfr
3
- Version: 4.1.0
4
- Summary: TNFR canónica: dinámica glífica modular sobre redes.
3
+ Version: 4.5.0
4
+ Summary: Canonical TNFR: modular glyph-based dynamics on networks.
5
5
  Author: fmg
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://pypi.org/project/tnfr/
8
8
  Project-URL: Repository, https://github.com/fermga/Teoria-de-la-naturaleza-fractal-resonante-TNFR-
9
- Keywords: TNFR,fractal resonante,resonancia,glifos,networkx,dinámica,coherencia,EPI,Kuramoto
9
+ Keywords: TNFR,resonant fractal,resonance,glyphs,networkx,dynamics,coherence,EPI,Kuramoto
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3 :: Only
12
12
  Classifier: Programming Language :: Python :: 3.9
@@ -25,7 +25,13 @@ License-File: LICENSE.md
25
25
  Requires-Dist: networkx>=2.6
26
26
  Dynamic: license-file
27
27
 
28
- # General Project Structure
28
+ # TNFR Python Project
29
+
30
+ Reference implementation of the Resonant Fractal Nature Theory (TNFR).
31
+ It models glyph-driven dynamics on NetworkX graphs, providing a modular
32
+ engine to simulate coherent reorganization processes.
33
+
34
+ ## General Project Structure
29
35
 
30
36
  * **Package entry point.** `__init__.py` registers modules under short names to avoid circular imports and exposes the public API: `preparar_red`, `step`, `run`, and observation utilities.
31
37
 
@@ -0,0 +1,28 @@
1
+ tnfr/__init__.py,sha256=c3fypVJdVZeV-AEZEPsr4NZZRwH9reGEBwoNnT9dP88,2788
2
+ tnfr/cli.py,sha256=7OR3dlWUVjEKVH_itH-zYq8WQculXwSdCDDvmQH15kY,14372
3
+ tnfr/config.py,sha256=bFc5AnLVoF0oUrSedAi5WpD6oCvn4hhLHO7WGgslg2M,1303
4
+ tnfr/constants.py,sha256=XoUlTuUaQgQ6BbHzKSIHijqRMf9Gb3Avjo7_CLicgps,11570
5
+ tnfr/dynamics.py,sha256=An0MlAJVhCy5uCQsl3c_PZKdAKBZojgRCnTAQzfaahg,32799
6
+ tnfr/gamma.py,sha256=U1yXbv4ef9VSwXirjRlcwdNr78Ah5LstAsg5WIkRQxY,4083
7
+ tnfr/grammar.py,sha256=vz5F0P3IfvA6HassRcoD327hBP5vCUw-xPSTsPmqwhQ,5363
8
+ tnfr/helpers.py,sha256=KT9_CAz3Z-WRKdARyGnsQ16m-p2z-l1-Y6UhVyY8tcU,8323
9
+ tnfr/main.py,sha256=XqjI1YEdF-OqRzTMa5dYIxCig4qyAR-l1FPcyxpC8WY,1926
10
+ tnfr/metrics.py,sha256=MTp0YifWdycW-jUFWhvxbJx-labWUR0thTnS0sxifrg,20259
11
+ tnfr/node.py,sha256=A3nlaW_lwG0Q1dj6HPmyjfccWfZt4rwnzqPxTglu-Sk,6184
12
+ tnfr/observers.py,sha256=PTw3hxk7KD-Yx_CvCIU09icuhyYD6uNU6SvF80UvP-Y,5354
13
+ tnfr/ontosim.py,sha256=9GfEtiLIdJOPJUTufcq_MssAA9J8AfChHU6HKb3DIJY,5628
14
+ tnfr/operators.py,sha256=TSY2LNIC-um3iSwniYiyu6oumnDwiES2zwb2B7ZbEZE,20536
15
+ tnfr/presets.py,sha256=qFuDxlexc_kw--3VRaOx3cfyL6vPEOX_UVsJd2DNWAE,998
16
+ tnfr/program.py,sha256=eim7D8zsbbkGDWbODag-0VKG44jEYioX4Sl6KRwgVtw,6038
17
+ tnfr/scenarios.py,sha256=QkUdCHp5R5T44fgfGppJ8dHzZa6avdNTNsYJbya_7XM,1165
18
+ tnfr/sense.py,sha256=9ABkqHjwu5pxoakddZwANpp9xy_NNo4frm9NGTg1GXQ,6482
19
+ tnfr/structural.py,sha256=hE5_l7cuiPad9AuFVrFtnkJ8A8eL_e69gUN5VknkQiI,5155
20
+ tnfr/trace.py,sha256=e_xdOOMZ5rqXOcdw99h8X1RnVuf_s9AeTzncqS551hE,4735
21
+ tnfr/types.py,sha256=CnSwzzh9d0WgqB128y71iNWiiAA7Sf-eJ_v1xHMAwLo,507
22
+ tnfr/validators.py,sha256=tCsz9A8OvEKiBZX9wvvri_C894XXdWfkbEZ6qqpcdNg,1169
23
+ tnfr-4.5.0.dist-info/licenses/LICENSE.md,sha256=SRvvhXLrKtseuK6DARbuJffuXOXqAyk3wvF2n0t1SWA,1109
24
+ tnfr-4.5.0.dist-info/METADATA,sha256=k7dlvh_IujIeTJfUfQXvCV9h6RbbtQvIbsJ3ylKUHTo,6301
25
+ tnfr-4.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
+ tnfr-4.5.0.dist-info/entry_points.txt,sha256=j4-QRHqeT2WnchHe_mvK7npGTLjlyfLpvRONFe9Z4MU,39
27
+ tnfr-4.5.0.dist-info/top_level.txt,sha256=Q2HJnvc5Rt2VHwVvyBTnNPT4SfmJWnCj7XUxxEvQa7c,5
28
+ tnfr-4.5.0.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- tnfr/__init__.py,sha256=93fkcY9lteZyKkZuWJAB-hvDEE2q0ZYgk16qCBXe50A,1988
2
- tnfr/cli.py,sha256=PVU95Iik_3bn-4UrLr8H7HHGme1X0qYnTuVv_AOp9FA,7484
3
- tnfr/constants.py,sha256=EPFW7howB2IsyeGRc8hyzO3s6sgRkM78lWB7S3BJXq4,9147
4
- tnfr/dynamics.py,sha256=SIf6cTj6_Wd6DRKJRYjE-iBuaxfDRpHu-j14wzlB-J0,25598
5
- tnfr/gamma.py,sha256=Ccsr2yKA7f8VlVqKgckQpGAEISlBw8i3FBeNPxfS-AQ,3048
6
- tnfr/grammar.py,sha256=vz5F0P3IfvA6HassRcoD327hBP5vCUw-xPSTsPmqwhQ,5363
7
- tnfr/helpers.py,sha256=CPEBkmFY6S4DKBf91EqW5vW_jTokOzg3HojCx28KxTU,7327
8
- tnfr/main.py,sha256=XqjI1YEdF-OqRzTMa5dYIxCig4qyAR-l1FPcyxpC8WY,1926
9
- tnfr/metrics.py,sha256=XusywHo1-GgLqYy1Jz43Xc4zRsggrn_DwoaFWfAy82M,6640
10
- tnfr/observers.py,sha256=PTw3hxk7KD-Yx_CvCIU09icuhyYD6uNU6SvF80UvP-Y,5354
11
- tnfr/ontosim.py,sha256=9GfEtiLIdJOPJUTufcq_MssAA9J8AfChHU6HKb3DIJY,5628
12
- tnfr/operators.py,sha256=QYsGf9R3lr8WdR0kanLqGYqZ-Lw-RWzjqKKTczgtyEw,13741
13
- tnfr/presets.py,sha256=rN6kDnI59DfYCL0YuYQr0yEu8jZaCFezGBnIWn6HFn8,737
14
- tnfr/program.py,sha256=eim7D8zsbbkGDWbODag-0VKG44jEYioX4Sl6KRwgVtw,6038
15
- tnfr/scenarios.py,sha256=FgsXzA6ENRkalENnG5ZuBNFguEGaKZTM6y0quW5fJI4,808
16
- tnfr/sense.py,sha256=1yVlphcs4o_uOQ51MDqW9dphydMiDNKv1Z1EdgbJgpU,6778
17
- tnfr/trace.py,sha256=bY0GU5I5yvHKSDOUQ0m5ICaXScYHzCM5xOWqTO68VO0,5026
18
- tnfr/types.py,sha256=xyFHp0PptEqPNUekAFH6DcAnyMx4bCQutMyFUXMd2sA,457
19
- tnfr-4.1.0.dist-info/licenses/LICENSE.md,sha256=SRvvhXLrKtseuK6DARbuJffuXOXqAyk3wvF2n0t1SWA,1109
20
- tnfr-4.1.0.dist-info/METADATA,sha256=cuR46do-ZJA5_OiZy4MNCwwbsEAi6DYD0HcRVTTYIz8,6075
21
- tnfr-4.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
- tnfr-4.1.0.dist-info/entry_points.txt,sha256=j4-QRHqeT2WnchHe_mvK7npGTLjlyfLpvRONFe9Z4MU,39
23
- tnfr-4.1.0.dist-info/top_level.txt,sha256=Q2HJnvc5Rt2VHwVvyBTnNPT4SfmJWnCj7XUxxEvQa7c,5
24
- tnfr-4.1.0.dist-info/RECORD,,
File without changes