genlayer-test 0.0.1__py3-none-any.whl → 0.1.0b1__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.
- genlayer_test-0.1.0b1.dist-info/METADATA +97 -0
- genlayer_test-0.1.0b1.dist-info/RECORD +15 -0
- genlayer_test-0.1.0b1.dist-info/entry_points.txt +2 -0
- genlayer_test-0.1.0b1.dist-info/top_level.txt +1 -0
- gltest/__init__.py +18 -0
- gltest/artifacts/__init__.py +3 -0
- gltest/artifacts/contract.py +52 -0
- gltest/assertions.py +17 -0
- gltest/cli.py +5 -0
- gltest/exceptions.py +4 -0
- gltest/glchain/__init__.py +16 -0
- gltest/glchain/account.py +17 -0
- gltest/glchain/client.py +15 -0
- gltest/glchain/contract.py +192 -0
- genlayer_test-0.0.1.dist-info/METADATA +0 -17
- genlayer_test-0.0.1.dist-info/RECORD +0 -4
- genlayer_test-0.0.1.dist-info/top_level.txt +0 -1
- {genlayer_test-0.0.1.dist-info → genlayer_test-0.1.0b1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: genlayer-test
|
3
|
+
Version: 0.1.0b1
|
4
|
+
Summary: GenLayer Testing Suite
|
5
|
+
Author: GenLayer
|
6
|
+
License-Expression: MIT
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
8
|
+
Classifier: Intended Audience :: Developers
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
14
|
+
Classifier: Topic :: Software Development :: Testing
|
15
|
+
Requires-Python: >=3.8
|
16
|
+
Description-Content-Type: text/markdown
|
17
|
+
Requires-Dist: pytest
|
18
|
+
Requires-Dist: genlayer-py==0.1.0b1
|
19
|
+
|
20
|
+
# GenLayer Testing Suite
|
21
|
+
|
22
|
+
[](https://opensource.org/license/mit/)
|
23
|
+
[](https://discord.gg/VpfmXEMN66)
|
24
|
+
[](https://x.com/GenLayer)
|
25
|
+
|
26
|
+
|
27
|
+
## About
|
28
|
+
|
29
|
+
The GenLayer Testing Suite is a powerful testing framework designed to streamline the development and validation of intelligent contracts within the GenLayer ecosystem. Built on top of [pytest](https://docs.pytest.org/en/stable/), this suite provides developers with a comprehensive set of tools for deploying, interacting with, and testing intelligent contracts efficiently in a simulated GenLayer environment.
|
30
|
+
|
31
|
+
|
32
|
+
## Prerequisites
|
33
|
+
|
34
|
+
Before installing GenLayer Testing Suite, ensure you have the following prerequisites installed:
|
35
|
+
|
36
|
+
- Python (>=3.8)
|
37
|
+
- GenLayer Studio (Docker deployment)
|
38
|
+
- pip (Python package installer)
|
39
|
+
|
40
|
+
## 🛠️ Installation and Usage
|
41
|
+
|
42
|
+
### Installation Options
|
43
|
+
|
44
|
+
1. Install from PyPI (recommended):
|
45
|
+
```bash
|
46
|
+
$ pip install genlayer-test
|
47
|
+
```
|
48
|
+
|
49
|
+
2. Install from source:
|
50
|
+
```bash
|
51
|
+
$ git clone https://github.com/yeagerai/genlayer-testing-suite
|
52
|
+
$ cd genlayer-testing-suite
|
53
|
+
$ pip install -e .
|
54
|
+
```
|
55
|
+
|
56
|
+
|
57
|
+
### Running Tests
|
58
|
+
|
59
|
+
1. Run all tests:
|
60
|
+
```bash
|
61
|
+
$ gltest
|
62
|
+
```
|
63
|
+
|
64
|
+
2. Run specific test file:
|
65
|
+
```bash
|
66
|
+
$ gltest tests/test_mycontract.py
|
67
|
+
```
|
68
|
+
|
69
|
+
3. Run tests with specific markers:
|
70
|
+
```bash
|
71
|
+
$ gltest -m "integration"
|
72
|
+
```
|
73
|
+
|
74
|
+
4. Run tests with verbose output:
|
75
|
+
```bash
|
76
|
+
$ gltest -v
|
77
|
+
```
|
78
|
+
|
79
|
+
For more detailed information and advanced usage, please refer to our [documentation](https://docs.genlayer.com/api-references/genlayer-testing-suite).
|
80
|
+
|
81
|
+
## 🚀 Key Features
|
82
|
+
- **Pytest Integration** – Extends pytest to support intelligent contract testing, making it familiar and easy to adopt.
|
83
|
+
|
84
|
+
- **Account & Transaction Management** – Create, fund, and track accounts and transactions within the GenLayer Simulator.
|
85
|
+
|
86
|
+
- **Contract Deployment & Interaction** – Deploy contracts, call methods, and monitor events seamlessly.
|
87
|
+
|
88
|
+
- **CLI Compatibility** – Run tests directly from the command line, ensuring smooth integration with the GenLayer CLI.
|
89
|
+
|
90
|
+
- **State Injection & Consensus Simulation** – Modify contract states dynamically and simulate consensus scenarios for advanced testing.
|
91
|
+
|
92
|
+
- **Prompt Testing & Statistical Analysis** – Evaluate and statistically test prompts for AI-driven contract execution.
|
93
|
+
|
94
|
+
- **Scalability to Security & Audit Tools** – Designed to extend into security testing and smart contract auditing.
|
95
|
+
|
96
|
+
|
97
|
+
By leveraging the GenLayer Testing Suite, developers can ensure their contracts perform reliably and securely before deployment on the live network.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
gltest/__init__.py,sha256=AK_YfRvwlhrOheOelUG8qIRG17on0-nFCF747dopg2w,332
|
2
|
+
gltest/assertions.py,sha256=wrT5R8yLAxm0B1EzmdZjOK5gBriugIrdtOIt9Qi0G-k,591
|
3
|
+
gltest/cli.py,sha256=Ti2-0Ev1x5_cM0D1UKqdgaDt80CDHEQGtdRne2qLm4M,53
|
4
|
+
gltest/exceptions.py,sha256=g7IF0qA8L0PdonXHTsbFnWx05U3jEYWOL3EFTW21IL4,95
|
5
|
+
gltest/artifacts/__init__.py,sha256=QCsQI8BSOHQwGWFxZVDRL-HA0Dyae3Re24E-yAZrflA,86
|
6
|
+
gltest/artifacts/contract.py,sha256=tOXv5rOGkoEeBndEp2wB9YnaTNvBRqACkGMkzC_tt4E,1761
|
7
|
+
gltest/glchain/__init__.py,sha256=wD2Hzdz_wKpu-mCAX5iBINZDCNdKezLVI2lywkSkjvw,400
|
8
|
+
gltest/glchain/account.py,sha256=vOXxcR09NkT6iFw_m49DW1nVC7kP8wf23555iPPXRkw,402
|
9
|
+
gltest/glchain/client.py,sha256=U_JJJQ0V8oLPLeIbGpFcHOtnCVPJIoN9DMlvHSD1C0I,355
|
10
|
+
gltest/glchain/contract.py,sha256=pt-LdF8k8jWCmWhU2-LI72w-vdVyi2g9Ux59MR8W3Es,6469
|
11
|
+
genlayer_test-0.1.0b1.dist-info/METADATA,sha256=2nkEPJTInKomnfv-DnvCJoDTkyWrImvUsWyctCqu8mo,3409
|
12
|
+
genlayer_test-0.1.0b1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
13
|
+
genlayer_test-0.1.0b1.dist-info/entry_points.txt,sha256=JfU-i_s5OciWVqtb0Ye5Pu_A1jJkDkitwLn4esJPZz4,43
|
14
|
+
genlayer_test-0.1.0b1.dist-info/top_level.txt,sha256=xTaZGUzUZ0S1U6_lp5SwNMQWZJKFaXI1Yg8gGKFKFJg,7
|
15
|
+
genlayer_test-0.1.0b1.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
gltest
|
gltest/__init__.py
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
from gltest.glchain import (
|
2
|
+
create_account,
|
3
|
+
create_accounts,
|
4
|
+
get_contract_factory,
|
5
|
+
get_gl_client,
|
6
|
+
default_account,
|
7
|
+
accounts,
|
8
|
+
)
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
"find_contract_definition",
|
12
|
+
"create_account",
|
13
|
+
"create_accounts",
|
14
|
+
"get_contract_factory",
|
15
|
+
"get_gl_client",
|
16
|
+
"default_account",
|
17
|
+
"accounts",
|
18
|
+
]
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import ast
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Optional
|
4
|
+
from dataclasses import dataclass
|
5
|
+
|
6
|
+
|
7
|
+
@dataclass
|
8
|
+
class ContractDefinition:
|
9
|
+
"""Class that represents a contract definition from a .gpy file."""
|
10
|
+
|
11
|
+
contract_name: str
|
12
|
+
contract_code: str
|
13
|
+
source_file: str
|
14
|
+
ast_node: ast.ClassDef
|
15
|
+
|
16
|
+
|
17
|
+
def find_contract_definition(
|
18
|
+
contract_name: str, relative_contracts_dir: str = "contracts"
|
19
|
+
) -> Optional[ContractDefinition]:
|
20
|
+
"""
|
21
|
+
Search in the contracts directory for a contract definition.
|
22
|
+
TODO: Make this more robust to handle imports and other files.
|
23
|
+
"""
|
24
|
+
# Use provided directory or default to 'contracts' in current working directory
|
25
|
+
contracts_dir = Path.cwd() / relative_contracts_dir
|
26
|
+
|
27
|
+
if not contracts_dir.exists():
|
28
|
+
raise FileNotFoundError(f"Contracts directory not found at: {contracts_dir}")
|
29
|
+
|
30
|
+
# Search through all .gpy files in the contracts directory
|
31
|
+
for file_path in contracts_dir.glob("*.gpy"):
|
32
|
+
try:
|
33
|
+
# Read the file content
|
34
|
+
with open(file_path, "r") as f:
|
35
|
+
content = f.read()
|
36
|
+
|
37
|
+
# Parse the content into an AST
|
38
|
+
tree = ast.parse(content)
|
39
|
+
|
40
|
+
# Search for class definitions
|
41
|
+
for node in ast.walk(tree):
|
42
|
+
if isinstance(node, ast.ClassDef) and node.name == contract_name:
|
43
|
+
# Found the contract class
|
44
|
+
return ContractDefinition(
|
45
|
+
contract_name=contract_name,
|
46
|
+
source_file=str(file_path),
|
47
|
+
contract_code=content,
|
48
|
+
ast_node=node,
|
49
|
+
)
|
50
|
+
except Exception as e:
|
51
|
+
raise ValueError(f"Error reading file {file_path}: {e}")
|
52
|
+
return None
|
gltest/assertions.py
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
from genlayer_py.types import GenLayerTransaction
|
2
|
+
|
3
|
+
|
4
|
+
def tx_execution_succeeded(result: GenLayerTransaction) -> bool:
|
5
|
+
if "consensus_data" not in result:
|
6
|
+
return False
|
7
|
+
if "leader_receipt" not in result["consensus_data"]:
|
8
|
+
return False
|
9
|
+
if "execution_result" not in result["consensus_data"]["leader_receipt"]:
|
10
|
+
return False
|
11
|
+
execution_result = result["consensus_data"]["leader_receipt"]["execution_result"]
|
12
|
+
return execution_result == "SUCCESS"
|
13
|
+
|
14
|
+
|
15
|
+
def tx_execution_failed(result: GenLayerTransaction) -> bool:
|
16
|
+
return not tx_execution_succeeded(result)
|
17
|
+
|
gltest/cli.py
ADDED
gltest/exceptions.py
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
from .contract import Contract, ContractFactory, get_contract_factory
|
2
|
+
from .client import gl_client, get_gl_client
|
3
|
+
from .account import create_accounts, create_account, accounts, default_account
|
4
|
+
|
5
|
+
|
6
|
+
__all__ = [
|
7
|
+
"Contract",
|
8
|
+
"ContractFactory",
|
9
|
+
"get_contract_factory",
|
10
|
+
"gl_client",
|
11
|
+
"create_account",
|
12
|
+
"default_account",
|
13
|
+
"accounts",
|
14
|
+
"create_accounts",
|
15
|
+
"get_gl_client",
|
16
|
+
]
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from genlayer_py import create_account
|
2
|
+
|
3
|
+
|
4
|
+
def create_accounts(n_accounts: int):
|
5
|
+
"""
|
6
|
+
Create a list of accounts
|
7
|
+
"""
|
8
|
+
accounts = []
|
9
|
+
for _ in range(n_accounts):
|
10
|
+
accounts.append(create_account())
|
11
|
+
return accounts
|
12
|
+
|
13
|
+
# Accounts for testing
|
14
|
+
accounts = create_accounts(n_accounts=10)
|
15
|
+
|
16
|
+
# Default account to use for transaction handling, if not specified
|
17
|
+
default_account = accounts[0]
|
gltest/glchain/client.py
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
from genlayer_py.chains import localnet
|
2
|
+
from genlayer_py import create_client
|
3
|
+
from .account import default_account
|
4
|
+
from functools import lru_cache
|
5
|
+
|
6
|
+
# Create the client
|
7
|
+
gl_client = create_client(chain=localnet, account=default_account)
|
8
|
+
|
9
|
+
|
10
|
+
@lru_cache(maxsize=1)
|
11
|
+
def get_gl_client():
|
12
|
+
"""
|
13
|
+
Get the GenLayer client instance.
|
14
|
+
"""
|
15
|
+
return gl_client
|
@@ -0,0 +1,192 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from gltest.artifacts import find_contract_definition
|
3
|
+
from gltest.assertions import tx_execution_failed
|
4
|
+
from gltest.exceptions import DeploymentError
|
5
|
+
from .client import get_gl_client
|
6
|
+
from genlayer_py.types import CalldataEncodable, GenLayerTransaction
|
7
|
+
from eth_account.signers.local import LocalAccount
|
8
|
+
from typing import List, Any, Type, Optional, Dict, Callable
|
9
|
+
import types
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass
|
13
|
+
class Contract:
|
14
|
+
"""
|
15
|
+
Class to interact with a contract, its methods
|
16
|
+
are implemented dynamically at build time.
|
17
|
+
"""
|
18
|
+
|
19
|
+
address: str
|
20
|
+
account: Optional[LocalAccount] = None
|
21
|
+
|
22
|
+
@classmethod
|
23
|
+
def from_address_and_schema(
|
24
|
+
cls, address: str, schema: Dict[str, Any]
|
25
|
+
) -> "Contract":
|
26
|
+
"""
|
27
|
+
Build the methods from the schema.
|
28
|
+
"""
|
29
|
+
if not isinstance(schema, dict) or "methods" not in schema:
|
30
|
+
raise ValueError("Invalid schema: must contain 'methods' field")
|
31
|
+
|
32
|
+
instance = cls(address=address)
|
33
|
+
for method_name, method_info in schema["methods"].items():
|
34
|
+
if not isinstance(method_info, dict) or "readonly" not in method_info:
|
35
|
+
raise ValueError(
|
36
|
+
f"Invalid method info for {method_name}: must contain 'readonly' field"
|
37
|
+
)
|
38
|
+
setattr(
|
39
|
+
instance,
|
40
|
+
method_name,
|
41
|
+
types.MethodType(
|
42
|
+
cls.contract_method_factory(method_name, method_info["readonly"]),
|
43
|
+
instance,
|
44
|
+
),
|
45
|
+
)
|
46
|
+
return instance
|
47
|
+
|
48
|
+
def connect(self, account: LocalAccount) -> "Contract":
|
49
|
+
self.account = account
|
50
|
+
return self
|
51
|
+
|
52
|
+
@staticmethod
|
53
|
+
def contract_method_factory(method_name: str, read_only: bool) -> Callable:
|
54
|
+
"""
|
55
|
+
Create a function that interacts with a specific contract method.
|
56
|
+
"""
|
57
|
+
|
58
|
+
def read_contract_wrapper(
|
59
|
+
self,
|
60
|
+
args: Optional[List[CalldataEncodable]] = None,
|
61
|
+
) -> Any:
|
62
|
+
"""
|
63
|
+
Wrapper to the contract read method.
|
64
|
+
"""
|
65
|
+
client = get_gl_client()
|
66
|
+
return client.read_contract(
|
67
|
+
address=self.address,
|
68
|
+
function_name=method_name,
|
69
|
+
account=self.account,
|
70
|
+
args=args,
|
71
|
+
)
|
72
|
+
|
73
|
+
def write_contract_wrapper(
|
74
|
+
self,
|
75
|
+
args: Optional[List[CalldataEncodable]] = None,
|
76
|
+
value: int = 0,
|
77
|
+
consensus_max_rotations: Optional[int] = None,
|
78
|
+
leader_only: bool = False,
|
79
|
+
wait_interval: Optional[int] = None,
|
80
|
+
wait_retries: Optional[int] = None,
|
81
|
+
) -> GenLayerTransaction:
|
82
|
+
"""
|
83
|
+
Wrapper to the contract write method.
|
84
|
+
"""
|
85
|
+
client = get_gl_client()
|
86
|
+
tx_hash = client.write_contract(
|
87
|
+
address=self.address,
|
88
|
+
function_name=method_name,
|
89
|
+
account=self.account,
|
90
|
+
value=value,
|
91
|
+
consensus_max_rotations=consensus_max_rotations,
|
92
|
+
leader_only=leader_only,
|
93
|
+
args=args,
|
94
|
+
)
|
95
|
+
extra_args = {}
|
96
|
+
if wait_interval is not None:
|
97
|
+
extra_args["interval"] = wait_interval
|
98
|
+
if wait_retries is not None:
|
99
|
+
extra_args["retries"] = wait_retries
|
100
|
+
receipt = client.wait_for_transaction_receipt(
|
101
|
+
transaction_hash=tx_hash, status="FINALIZED", **extra_args
|
102
|
+
)
|
103
|
+
return receipt
|
104
|
+
|
105
|
+
return read_contract_wrapper if read_only else write_contract_wrapper
|
106
|
+
|
107
|
+
|
108
|
+
@dataclass
|
109
|
+
class ContractFactory:
|
110
|
+
"""
|
111
|
+
A factory for deploying contracts.
|
112
|
+
"""
|
113
|
+
|
114
|
+
contract_name: str
|
115
|
+
contract_code: str
|
116
|
+
|
117
|
+
@classmethod
|
118
|
+
def from_artifact(
|
119
|
+
cls: Type["ContractFactory"], contract_name: str
|
120
|
+
) -> "ContractFactory":
|
121
|
+
"""
|
122
|
+
Create a ContractFactory instance given the contract name.
|
123
|
+
"""
|
124
|
+
contract_info = find_contract_definition(contract_name)
|
125
|
+
if contract_info is None:
|
126
|
+
raise ValueError(
|
127
|
+
f"Contract {contract_name} not found in the contracts directory"
|
128
|
+
)
|
129
|
+
return cls(
|
130
|
+
contract_name=contract_name, contract_code=contract_info.contract_code
|
131
|
+
)
|
132
|
+
|
133
|
+
def deploy(
|
134
|
+
self,
|
135
|
+
args: List[Any] = [],
|
136
|
+
account: Optional[LocalAccount] = None,
|
137
|
+
consensus_max_rotations: Optional[int] = None,
|
138
|
+
leader_only: bool = False,
|
139
|
+
wait_interval: Optional[int] = None,
|
140
|
+
wait_retries: Optional[int] = None,
|
141
|
+
) -> Contract:
|
142
|
+
"""
|
143
|
+
Deploy the contract
|
144
|
+
"""
|
145
|
+
client = get_gl_client()
|
146
|
+
try:
|
147
|
+
tx_hash = client.deploy_contract(
|
148
|
+
code=self.contract_code,
|
149
|
+
args=args,
|
150
|
+
account=account,
|
151
|
+
consensus_max_rotations=consensus_max_rotations,
|
152
|
+
leader_only=leader_only,
|
153
|
+
)
|
154
|
+
extra_args = {}
|
155
|
+
if wait_interval is not None:
|
156
|
+
extra_args["interval"] = wait_interval
|
157
|
+
if wait_retries is not None:
|
158
|
+
extra_args["retries"] = wait_retries
|
159
|
+
tx_receipt = client.wait_for_transaction_receipt(
|
160
|
+
transaction_hash=tx_hash, status="FINALIZED", **extra_args
|
161
|
+
)
|
162
|
+
|
163
|
+
if (
|
164
|
+
not tx_receipt
|
165
|
+
or "data" not in tx_receipt
|
166
|
+
or "contract_address" not in tx_receipt["data"]
|
167
|
+
):
|
168
|
+
raise ValueError(
|
169
|
+
"Invalid transaction receipt: missing contract address"
|
170
|
+
)
|
171
|
+
|
172
|
+
if tx_execution_failed(tx_receipt):
|
173
|
+
raise ValueError(
|
174
|
+
f"Deployment transaction finalized with error: {tx_receipt}"
|
175
|
+
)
|
176
|
+
|
177
|
+
contract_address = tx_receipt["data"]["contract_address"]
|
178
|
+
schema = client.get_contract_schema(address=contract_address)
|
179
|
+
return Contract.from_address_and_schema(
|
180
|
+
address=contract_address, schema=schema
|
181
|
+
)
|
182
|
+
except Exception as e:
|
183
|
+
raise DeploymentError(
|
184
|
+
f"Failed to deploy contract {self.contract_name}: {str(e)}"
|
185
|
+
) from e
|
186
|
+
|
187
|
+
|
188
|
+
def get_contract_factory(contract_name: str) -> ContractFactory:
|
189
|
+
"""
|
190
|
+
Get a ContractFactory instance for a contract.
|
191
|
+
"""
|
192
|
+
return ContractFactory.from_artifact(contract_name)
|
@@ -1,17 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.4
|
2
|
-
Name: genlayer-test
|
3
|
-
Version: 0.0.1
|
4
|
-
Summary: A minimal empty PyPI package for genlayer-test
|
5
|
-
Author-email: GenLayer Labs Corp <tech@genlayer.com>
|
6
|
-
License: MIT
|
7
|
-
Requires-Python: >=3.6
|
8
|
-
Description-Content-Type: text/markdown
|
9
|
-
|
10
|
-
# GenLayer Test
|
11
|
-
|
12
|
-
This is a minimal empty Python package for genlayer-test.
|
13
|
-
|
14
|
-
## Installation
|
15
|
-
```bash
|
16
|
-
pip install genlayer-test
|
17
|
-
```
|
@@ -1,4 +0,0 @@
|
|
1
|
-
genlayer_test-0.0.1.dist-info/METADATA,sha256=s6JVDtHiKhzy8c3UKBLYxGJLhzgitAsegT_XClxJVWM,373
|
2
|
-
genlayer_test-0.0.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
3
|
-
genlayer_test-0.0.1.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
4
|
-
genlayer_test-0.0.1.dist-info/RECORD,,
|
@@ -1 +0,0 @@
|
|
1
|
-
|
File without changes
|