rowan-python 2.1.14__py3-none-any.whl → 2.1.16__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.
rowan/workflow.py CHANGED
@@ -72,6 +72,21 @@ class Workflow(BaseModel):
72
72
  def __repr__(self) -> str:
73
73
  return f"<Workflow name='{self.name}' created_at='{self.created_at}' uuid='{self.uuid}'>"
74
74
 
75
+ def __str__(self) -> str:
76
+ created = self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else "N/A"
77
+ elapsed = f"{self.elapsed:.2f}s" if self.elapsed is not None else "not submitted"
78
+ status = self.status.name.lower() if self.status is not None else "not submitted"
79
+ lines = [
80
+ f"Workflow: {self.name}",
81
+ f" UUID: {self.uuid}",
82
+ f" Type: {self.workflow_type}",
83
+ f" Status: {status}",
84
+ f" Created: {created}",
85
+ f" Elapsed: {elapsed}",
86
+ f" Credits charged: {self.credits_charged}",
87
+ ]
88
+ return "\n".join(lines)
89
+
75
90
  def fetch_latest(self, in_place: bool = False) -> Self:
76
91
  """
77
92
  Loads workflow data from the database and updates the current instance.
@@ -371,13 +386,12 @@ def batch_submit_workflow(
371
386
  if names:
372
387
  if initial_molecules and len(names) != len(initial_molecules):
373
388
  logger.warning(
374
- "The number of names is not the same as the number of initial "
375
- "molecules. Generic names of the form 'Batch Workflow {i}' "
376
- "will be used."
389
+ "The number of names is not the same as the number of initial molecules. "
390
+ "Generic names of the form 'Batch Workflow {i}' will be used."
377
391
  )
378
392
  if initial_smileses and len(names) != len(initial_smileses):
379
393
  logger.warning(
380
- "The number of names is not the same as the number of initial SMILES."
394
+ "The number of names is not the same as the number of initial SMILES. "
381
395
  "Generic names of the form 'Batch Workflow {i}' will be used."
382
396
  )
383
397
 
@@ -1166,18 +1180,20 @@ def submit_protein_cofolding_workflow(
1166
1180
 
1167
1181
  :param initial_protein_sequences: The sequences of the proteins to be cofolded.
1168
1182
  :param initial_smiles_list: A list of SMILES strings for the ligands to be cofolded with.
1169
- :param ligand_binding_affinity_index: The index of the ligand for which to compute the binding affinity.
1183
+ :param ligand_binding_affinity_index: The index of the ligand for which to compute
1184
+ the binding affinity.
1170
1185
  :param use_msa_server: Whether to use the MSA server for the computation.
1171
1186
  :param use_potentials: Whether to use potentials for the computation.
1172
- :param do_pose_refinement: whether to optimize non-rotatable bonds in output poses
1173
- :param compute_strain: whether to compute the strain of the pose (if `pose_refinement` is enabled)
1187
+ :param do_pose_refinement: Whether to optimize non-rotatable bonds in output poses.
1188
+ :param compute_strain: Whether to compute the strain of the pose
1189
+ (if `pose_refinement` is enabled).
1174
1190
  :param name: The name of the workflow.
1175
1191
  :param model: The model to use for the computation.
1176
1192
  :param folder_uuid: The UUID of the folder to store the workflow in.
1177
1193
  :param max_credits: The maximum number of credits to use for the workflow.
1178
1194
  :return: A Workflow object representing the submitted workflow.
1179
1195
  :raises requests.HTTPError: if the request to the API fails.
1180
- """ # noqa: E501
1196
+ """
1181
1197
 
1182
1198
  workflow = stjames.ProteinCofoldingWorkflow(
1183
1199
  use_msa_server=use_msa_server,
@@ -1338,9 +1354,9 @@ def submit_nmr_workflow(
1338
1354
  max_credits: int | None = None,
1339
1355
  ) -> Workflow:
1340
1356
  """
1341
- Submits an NMR-prediction workflow to the API.
1357
+ Submits a Nuclear Magnetic Resonance (NMR) prediction workflow to the API.
1342
1358
 
1343
- :param initial_molecule: The molecule used in the scan.
1359
+ :param initial_molecule: The molecule to predict NMR spectra for.
1344
1360
  :param solvent: The solvent in which to compute NMR spectra.
1345
1361
  :param do_csearch: Whether to perform a conformational search on the input structure.
1346
1362
  :param do_optimization: Whether to perform an optimization on the input structure.
@@ -1491,7 +1507,7 @@ def submit_pose_analysis_md_workflow(
1491
1507
  max_credits: int | None = None,
1492
1508
  ) -> Workflow:
1493
1509
  """
1494
- Submits a pose-analysis MD workflow to the API.
1510
+ Submits a Pose-Analysis Molecular Dynamics (MD) workflow to the API.
1495
1511
 
1496
1512
  :param protein: The *holo* protein on which MD will be run.
1497
1513
  Can be input as a UUID or a Protein object.
@@ -1618,7 +1634,7 @@ def submit_msa_workflow(
1618
1634
  max_credits: int | None = None,
1619
1635
  ) -> Workflow:
1620
1636
  """
1621
- Submits a multiple sequence alignment (MSA) workflow to the API.
1637
+ Submits a Multiple Sequence Alignment (MSA) workflow to the API.
1622
1638
 
1623
1639
  :param initial_protein_sequences: List of protein sequences to align, as ProteinSequence objects
1624
1640
  sor strings.
@@ -1655,9 +1671,10 @@ def submit_admet_workflow(
1655
1671
  max_credits: int | None = None,
1656
1672
  ) -> Workflow:
1657
1673
  """
1658
- Submits an ADMET workflow to the API.
1674
+ Submits an Absorption, Distribution, Metabolism, Excretion, and Toxicity (ADMET)
1675
+ workflow to the API.
1659
1676
 
1660
- :param initial_smiles: The molecule used in the workflow.
1677
+ :param initial_smiles: The molecule to calculate ADMET properties for.
1661
1678
  :param name: The name of the workflow.
1662
1679
  :param folder_uuid: The UUID of the folder to store the workflow in.
1663
1680
  :param max_credits: The maximum number of credits to use for the workflow.
@@ -1830,7 +1847,7 @@ def submit_protein_md_workflow(
1830
1847
  max_credits: int | None = None,
1831
1848
  ) -> Workflow:
1832
1849
  """
1833
- Submits a pose-analysis MD workflow to the API.
1850
+ Submits a Protein Molecular Dynamics (MD) workflow to the API.
1834
1851
 
1835
1852
  :param protein: The *holo* protein on which MD will be run.
1836
1853
  Can be input as a UUID or a Protein object.
@@ -1884,3 +1901,330 @@ def submit_protein_md_workflow(
1884
1901
  response = client.post("/workflow", json=data)
1885
1902
  response.raise_for_status()
1886
1903
  return Workflow(**response.json())
1904
+
1905
+
1906
+ def submit_bde_workflow(
1907
+ initial_molecule: dict[str, Any] | StJamesMolecule | RdkitMol,
1908
+ mode: str = "rapid",
1909
+ atoms: list[int] | None = None,
1910
+ all_CH: bool = False,
1911
+ all_CX: bool = False,
1912
+ name: str = "BDE Workflow",
1913
+ folder_uuid: str | None = None,
1914
+ max_credits: int | None = None,
1915
+ ) -> Workflow:
1916
+ """
1917
+ Submits a bond dissociation energy (BDE) workflow to the API.
1918
+
1919
+ :param initial_molecule: The molecule to calculate BDEs for.
1920
+ :param mode: The mode to run the calculation in.
1921
+ :param atoms: List of atom indices (1-indexed) to dissociate.
1922
+ :param all_CH: Whether to dissociate all C-H bonds.
1923
+ :param all_CX: Whether to dissociate all C-X bonds (X = halogen).
1924
+ :param name: The name of the workflow.
1925
+ :param folder_uuid: The UUID of the folder to place the workflow in.
1926
+ :param max_credits: The maximum number of credits to use for the workflow.
1927
+ :return: A Workflow object representing the submitted workflow.
1928
+ :raises requests.HTTPError: if the request to the API fails.
1929
+ """
1930
+ if isinstance(initial_molecule, StJamesMolecule):
1931
+ initial_molecule = initial_molecule.model_dump(mode="json")
1932
+ elif isinstance(initial_molecule, RdkitMol):
1933
+ initial_molecule = StJamesMolecule.from_rdkit(initial_molecule, cid=0).model_dump(
1934
+ mode="json"
1935
+ )
1936
+
1937
+ workflow = stjames.BDEWorkflow(
1938
+ initial_molecule=initial_molecule,
1939
+ mode=mode,
1940
+ atoms=atoms or [],
1941
+ all_CH=all_CH,
1942
+ all_CX=all_CX,
1943
+ )
1944
+
1945
+ data = {
1946
+ "name": name,
1947
+ "folder_uuid": folder_uuid,
1948
+ "workflow_type": "bde",
1949
+ "workflow_data": workflow.model_dump(mode="json"),
1950
+ "initial_molecule": initial_molecule,
1951
+ "max_credits": max_credits,
1952
+ }
1953
+
1954
+ with api_client() as client:
1955
+ response = client.post("/workflow", json=data)
1956
+ response.raise_for_status()
1957
+ return Workflow(**response.json())
1958
+
1959
+
1960
+ def submit_electronic_properties_workflow(
1961
+ initial_molecule: dict[str, Any] | StJamesMolecule | RdkitMol,
1962
+ method: stjames.Method | str = "b97_3c",
1963
+ basis_set: str | None = None,
1964
+ compute_density_cube: bool = True,
1965
+ compute_electrostatic_potential_cube: bool = True,
1966
+ compute_num_occupied_orbitals: int = 1,
1967
+ compute_num_virtual_orbitals: int = 1,
1968
+ name: str = "Electronic Properties Workflow",
1969
+ folder_uuid: str | None = None,
1970
+ max_credits: int | None = None,
1971
+ ) -> Workflow:
1972
+ """
1973
+ Submits an electronic properties workflow to the API.
1974
+
1975
+ :param initial_molecule: The molecule to calculate electronic properties for.
1976
+ :param method: The method to use for the calculation.
1977
+ :param basis_set: The basis set to use (if any).
1978
+ :param compute_density_cube: Whether to compute the density cube.
1979
+ :param compute_electrostatic_potential_cube: Whether to compute the electrostatic
1980
+ potential cube.
1981
+ :param compute_num_occupied_orbitals: Number of occupied orbitals to save.
1982
+ :param compute_num_virtual_orbitals: Number of virtual orbitals to save.
1983
+ :param name: The name of the workflow.
1984
+ :param folder_uuid: The UUID of the folder to place the workflow in.
1985
+ :param max_credits: The maximum number of credits to use for the workflow.
1986
+ :return: A Workflow object representing the submitted workflow.
1987
+ :raises requests.HTTPError: if the request to the API fails.
1988
+ """
1989
+ if isinstance(initial_molecule, StJamesMolecule):
1990
+ initial_molecule = initial_molecule.model_dump(mode="json")
1991
+ elif isinstance(initial_molecule, RdkitMol):
1992
+ initial_molecule = StJamesMolecule.from_rdkit(initial_molecule, cid=0).model_dump(
1993
+ mode="json"
1994
+ )
1995
+
1996
+ if isinstance(method, str):
1997
+ method = stjames.Method(method)
1998
+
1999
+ settings = stjames.Settings(method=method, basis_set=basis_set)
2000
+
2001
+ workflow = stjames.ElectronicPropertiesWorkflow(
2002
+ initial_molecule=initial_molecule,
2003
+ settings=settings,
2004
+ compute_density_cube=compute_density_cube,
2005
+ compute_electrostatic_potential_cube=compute_electrostatic_potential_cube,
2006
+ compute_num_occupied_orbitals=compute_num_occupied_orbitals,
2007
+ compute_num_virtual_orbitals=compute_num_virtual_orbitals,
2008
+ )
2009
+
2010
+ data = {
2011
+ "name": name,
2012
+ "folder_uuid": folder_uuid,
2013
+ "workflow_type": "electronic_properties",
2014
+ "workflow_data": workflow.model_dump(mode="json"),
2015
+ "initial_molecule": initial_molecule,
2016
+ "max_credits": max_credits,
2017
+ }
2018
+
2019
+ with api_client() as client:
2020
+ response = client.post("/workflow", json=data)
2021
+ response.raise_for_status()
2022
+ return Workflow(**response.json())
2023
+
2024
+
2025
+ def submit_hydrogen_bond_basicity_workflow(
2026
+ initial_molecule: dict[str, Any] | StJamesMolecule | RdkitMol,
2027
+ do_csearch: bool = True,
2028
+ do_optimization: bool = True,
2029
+ name: str = "Hydrogen Bond Basicity Workflow",
2030
+ folder_uuid: str | None = None,
2031
+ max_credits: int | None = None,
2032
+ ) -> Workflow:
2033
+ """
2034
+ Submits a hydrogen bond basicity workflow to the API.
2035
+
2036
+ :param initial_molecule: The molecule to calculate hydrogen bond basicity for.
2037
+ :param do_csearch: Whether to perform a conformational search.
2038
+ :param do_optimization: Whether to perform an optimization.
2039
+ :param name: The name of the workflow.
2040
+ :param folder_uuid: The UUID of the folder to place the workflow in.
2041
+ :param max_credits: The maximum number of credits to use for the workflow.
2042
+ :return: A Workflow object representing the submitted workflow.
2043
+ :raises requests.HTTPError: if the request to the API fails.
2044
+ """
2045
+ if isinstance(initial_molecule, StJamesMolecule):
2046
+ initial_molecule = initial_molecule.model_dump(mode="json")
2047
+ elif isinstance(initial_molecule, RdkitMol):
2048
+ initial_molecule = StJamesMolecule.from_rdkit(initial_molecule, cid=0).model_dump(
2049
+ mode="json"
2050
+ )
2051
+
2052
+ workflow = stjames.HydrogenBondBasicityWorkflow(
2053
+ initial_molecule=initial_molecule,
2054
+ do_csearch=do_csearch,
2055
+ do_optimization=do_optimization,
2056
+ )
2057
+
2058
+ data = {
2059
+ "name": name,
2060
+ "folder_uuid": folder_uuid,
2061
+ "workflow_type": "hydrogen_bond_basicity",
2062
+ "workflow_data": workflow.model_dump(mode="json"),
2063
+ "initial_molecule": initial_molecule,
2064
+ "max_credits": max_credits,
2065
+ }
2066
+
2067
+ with api_client() as client:
2068
+ response = client.post("/workflow", json=data)
2069
+ response.raise_for_status()
2070
+ return Workflow(**response.json())
2071
+
2072
+
2073
+ def submit_multistage_optimization_workflow(
2074
+ initial_molecule: dict[str, Any] | StJamesMolecule | RdkitMol,
2075
+ mode: str = "rapid",
2076
+ solvent: str | None = None,
2077
+ xtb_preopt: bool = True,
2078
+ transition_state: bool = False,
2079
+ frequencies: bool = False,
2080
+ name: str = "Multistage Optimization Workflow",
2081
+ folder_uuid: str | None = None,
2082
+ max_credits: int | None = None,
2083
+ ) -> Workflow:
2084
+ """
2085
+ Submits a multistage optimization workflow to the API.
2086
+
2087
+ :param initial_molecule: The molecule to optimize.
2088
+ :param mode: The mode to run the calculation in.
2089
+ :param solvent: The solvent to use for the final single-point calculation.
2090
+ :param xtb_preopt: Whether to pre-optimize with xTB.
2091
+ :param transition_state: Whether this is a transition state optimization.
2092
+ :param frequencies: Whether to calculate frequencies.
2093
+ :param name: The name of the workflow.
2094
+ :param folder_uuid: The UUID of the folder to place the workflow in.
2095
+ :param max_credits: The maximum number of credits to use for the workflow.
2096
+ :return: A Workflow object representing the submitted workflow.
2097
+ :raises requests.HTTPError: if the request to the API fails.
2098
+ """
2099
+ if isinstance(initial_molecule, StJamesMolecule):
2100
+ initial_molecule = initial_molecule.model_dump(mode="json")
2101
+ elif isinstance(initial_molecule, RdkitMol):
2102
+ initial_molecule = StJamesMolecule.from_rdkit(initial_molecule, cid=0).model_dump(
2103
+ mode="json"
2104
+ )
2105
+
2106
+ workflow = stjames.MultiStageOptWorkflow(
2107
+ initial_molecule=initial_molecule,
2108
+ mode=mode,
2109
+ solvent=solvent,
2110
+ xtb_preopt=xtb_preopt,
2111
+ transition_state=transition_state,
2112
+ frequencies=frequencies,
2113
+ )
2114
+
2115
+ data = {
2116
+ "name": name,
2117
+ "folder_uuid": folder_uuid,
2118
+ "workflow_type": "multistage_opt",
2119
+ "workflow_data": workflow.model_dump(mode="json"),
2120
+ "initial_molecule": initial_molecule,
2121
+ "max_credits": max_credits,
2122
+ }
2123
+
2124
+ with api_client() as client:
2125
+ response = client.post("/workflow", json=data)
2126
+ response.raise_for_status()
2127
+ return Workflow(**response.json())
2128
+
2129
+
2130
+ def submit_spin_states_workflow(
2131
+ initial_molecule: dict[str, Any] | StJamesMolecule | RdkitMol,
2132
+ states: list[int],
2133
+ mode: str = "rapid",
2134
+ solvent: str | None = None,
2135
+ xtb_preopt: bool = True,
2136
+ frequencies: bool = False,
2137
+ name: str = "Spin States Workflow",
2138
+ folder_uuid: str | None = None,
2139
+ max_credits: int | None = None,
2140
+ ) -> Workflow:
2141
+ """
2142
+ Submits a spin states workflow to the API.
2143
+
2144
+ :param initial_molecule: The molecule to calculate spin states for.
2145
+ :param states: List of multiplicities to calculate
2146
+ (e.g., [1, 3, 5] for singlet, triplet, quintet).
2147
+ :param mode: The mode to run the calculation in.
2148
+ :param solvent: The solvent to use for the calculation.
2149
+ :param xtb_preopt: Whether to pre-optimize with xTB.
2150
+ :param frequencies: Whether to calculate frequencies.
2151
+ :param name: The name of the workflow.
2152
+ :param folder_uuid: The UUID of the folder to place the workflow in.
2153
+ :param max_credits: The maximum number of credits to use for the workflow.
2154
+ :return: A Workflow object representing the submitted workflow.
2155
+ :raises requests.HTTPError: if the request to the API fails.
2156
+ """
2157
+ if isinstance(initial_molecule, StJamesMolecule):
2158
+ initial_molecule = initial_molecule.model_dump(mode="json")
2159
+ elif isinstance(initial_molecule, RdkitMol):
2160
+ initial_molecule = StJamesMolecule.from_rdkit(initial_molecule, cid=0).model_dump(
2161
+ mode="json"
2162
+ )
2163
+
2164
+ workflow = stjames.SpinStatesWorkflow(
2165
+ initial_molecule=initial_molecule,
2166
+ states=states,
2167
+ mode=mode,
2168
+ solvent=solvent,
2169
+ xtb_preopt=xtb_preopt,
2170
+ frequencies=frequencies,
2171
+ )
2172
+
2173
+ data = {
2174
+ "name": name,
2175
+ "folder_uuid": folder_uuid,
2176
+ "workflow_type": "spin_states",
2177
+ "workflow_data": workflow.model_dump(mode="json"),
2178
+ "initial_molecule": initial_molecule,
2179
+ "max_credits": max_credits,
2180
+ }
2181
+
2182
+ with api_client() as client:
2183
+ response = client.post("/workflow", json=data)
2184
+ response.raise_for_status()
2185
+ return Workflow(**response.json())
2186
+
2187
+
2188
+ def submit_protein_binder_design_workflow(
2189
+ binder_design_input: dict[str, Any],
2190
+ protocol: str = "protein-anything",
2191
+ num_designs: int = 10,
2192
+ budget: int = 2,
2193
+ name: str = "Protein Binder Design Workflow",
2194
+ folder_uuid: str | None = None,
2195
+ max_credits: int | None = None,
2196
+ ) -> Workflow:
2197
+ """
2198
+ Submits a protein binder design workflow to the API.
2199
+
2200
+ :param binder_design_input: Input specification for the binder design (BoltzGenInput format).
2201
+ :param protocol: The protocol to use.
2202
+ :param num_designs: Number of designs to generate.
2203
+ :param budget: Number of designs to return in the final diversity-optimized set.
2204
+ :param name: The name of the workflow.
2205
+ :param folder_uuid: The UUID of the folder to place the workflow in.
2206
+ :param max_credits: The maximum number of credits to use for the workflow.
2207
+ :return: A Workflow object representing the submitted workflow.
2208
+ :raises requests.HTTPError: if the request to the API fails.
2209
+ """
2210
+ workflow = stjames.ProteinBinderDesignWorkflow(
2211
+ binder_design_input=binder_design_input,
2212
+ binder_design_settings={
2213
+ "protocol": protocol,
2214
+ "num_designs": num_designs,
2215
+ "budget": budget,
2216
+ },
2217
+ )
2218
+
2219
+ data = {
2220
+ "name": name,
2221
+ "folder_uuid": folder_uuid,
2222
+ "workflow_type": "protein_binder_design",
2223
+ "workflow_data": workflow.model_dump(mode="json"),
2224
+ "max_credits": max_credits,
2225
+ }
2226
+
2227
+ with api_client() as client:
2228
+ response = client.post("/workflow", json=data)
2229
+ response.raise_for_status()
2230
+ return Workflow(**response.json())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rowan-python
3
- Version: 2.1.14
3
+ Version: 2.1.16
4
4
  Summary: Rowan Python Library
5
5
  Project-URL: Homepage, https://github.com/rowansci/rowan-client
6
6
  Project-URL: Bug Tracker, https://github.com/rowansci/rowan-client/issues
@@ -11,7 +11,7 @@ Requires-Dist: httpx
11
11
  Requires-Dist: nest-asyncio
12
12
  Requires-Dist: rdkit
13
13
  Requires-Dist: setuptools
14
- Requires-Dist: stjames>=0.0.144
14
+ Requires-Dist: stjames>=0.0.155
15
15
  Description-Content-Type: text/markdown
16
16
 
17
17
  # Rowan Python Library
@@ -6,10 +6,10 @@ rowan/protein.py,sha256=mFSVCr-08bSikXBUtJtSWzjKcVAuBmTeRckn7JUHYSE,8810
6
6
  rowan/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  rowan/user.py,sha256=Dl--NPUPATKCs2VmILsW8HnLiunG0Lxr0n6mKuHm21U,3891
8
8
  rowan/utils.py,sha256=64II-cPOe_SFJK302Bm8hP62d_3_CgnTVYCbn3zKT7U,3334
9
- rowan/workflow.py,sha256=QQWHTZ_O3PF2WbegYLPIKiszOEYj1frPKbI8U-B1muc,72419
9
+ rowan/workflow.py,sha256=8HT80bQbzb7k4oPRNMdzH7vDrAX__BzzJ2pjzUA7ogk,85642
10
10
  rowan/rowan_rdkit/__init__.py,sha256=EATX2VRzywzKxqkpCUMTf7RNQLkWsfi5VcCNDW6EIiw,503
11
11
  rowan/rowan_rdkit/chem_utils.py,sha256=sKCzul2e0ldVYTBImhTwso7ddNgPKmvS-OmvCEjVJH0,34788
12
- rowan_python-2.1.14.dist-info/METADATA,sha256=GdXzsTQIY2SSuTUgVnerp8Cib41LCL2koTc1VuPvP5w,1601
13
- rowan_python-2.1.14.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
- rowan_python-2.1.14.dist-info/licenses/LICENSE,sha256=i05z7xEhyrg6f8j0lR3XYjShnF-MJGFQ-DnpsZ8yiVI,1084
15
- rowan_python-2.1.14.dist-info/RECORD,,
12
+ rowan_python-2.1.16.dist-info/METADATA,sha256=eGbLBIwJnW9Iy5J20hgX7cbrlb89mmXeWZskV8R3aWk,1601
13
+ rowan_python-2.1.16.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
+ rowan_python-2.1.16.dist-info/licenses/LICENSE,sha256=i05z7xEhyrg6f8j0lR3XYjShnF-MJGFQ-DnpsZ8yiVI,1084
15
+ rowan_python-2.1.16.dist-info/RECORD,,