castor-extractor 0.19.8__py3-none-any.whl → 0.20.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 castor-extractor might be problematic. Click here for more details.

CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
1
 
2
2
  # Changelog
3
3
 
4
+ ## 0.20.0 - 2024-09-23
5
+
6
+ * Switch to Tableau revamped connector
7
+
8
+ ## 0.19.9 - 2024-09-19
9
+
10
+ * Databricks: multithreading to retrieve column lineage
11
+
4
12
  ## 0.19.8 - 2024-09-18
5
13
 
6
14
  * Metabase: Handle duplicate dashboards
@@ -1,7 +1,8 @@
1
1
  import logging
2
2
  from argparse import ArgumentParser
3
3
 
4
- from castor_extractor.visualization import tableau # type: ignore
4
+ from castor_extractor.utils import parse_filled_arguments # type: ignore
5
+ from castor_extractor.visualization import tableau_revamp # type: ignore
5
6
 
6
7
  logging.basicConfig(level=logging.INFO, format="%(levelname)s - %(message)s")
7
8
 
@@ -19,29 +20,14 @@ def main():
19
20
 
20
21
  parser.add_argument("-b", "--server-url", help="Tableau server url")
21
22
  parser.add_argument("-i", "--site-id", help="Tableau site ID")
23
+
22
24
  parser.add_argument(
23
- "-s",
24
- "--safe-mode",
25
- help="Tableau safe mode",
25
+ "--with-pulse",
26
+ dest="with_pulse",
26
27
  action="store_true",
28
+ help="Extract Tableau Pulse assets: Metrics and Subscriptions",
27
29
  )
28
- parser.add_argument("-o", "--output", help="Directory to write to")
29
30
 
30
- args = parser.parse_args()
31
-
32
- client = tableau.ApiClient(
33
- user=args.user,
34
- password=args.password,
35
- token_name=args.token_name,
36
- token=args.token,
37
- server_url=args.server_url,
38
- site_id=args.site_id,
39
- safe_mode=args.safe_mode,
40
- )
41
-
42
- client.login()
31
+ parser.add_argument("-o", "--output", help="Directory to write to")
43
32
 
44
- tableau.extract_all(
45
- client,
46
- output_directory=args.output,
47
- )
33
+ tableau_revamp.extract_all(**parse_filled_arguments(parser))
@@ -34,7 +34,7 @@ from .pager import (
34
34
  PagerOnIdLogger,
35
35
  PagerStopStrategy,
36
36
  )
37
- from .retry import RetryStrategy, retry
37
+ from .retry import RetryStrategy, retry, retry_request
38
38
  from .safe import SafeMode, safe_mode
39
39
  from .store import AbstractStorage, LocalStorage
40
40
  from .string import decode_when_bytes, string_to_tuple
@@ -26,18 +26,18 @@ def _generate_payloads(
26
26
  params: Optional[dict],
27
27
  data: Optional[dict],
28
28
  pagination_params: Optional[dict],
29
- ) -> Tuple[dict, dict]:
29
+ ) -> Tuple[Optional[dict], Optional[dict]]:
30
30
  _pagination_params = pagination_params or {}
31
- params = params or {}
32
- data = data or {}
33
31
 
34
32
  if method == "GET":
33
+ params = params or {}
35
34
  params = {**params, **_pagination_params}
36
- elif method == "POST":
35
+ return data, params
36
+ if method == "POST":
37
+ data = data or {}
37
38
  data = {**data, **_pagination_params}
38
- else:
39
- raise ValueError(f"Method {method} is not yet supported")
40
- return data, params
39
+ return data, params
40
+ raise ValueError(f"Method {method} is not yet supported")
41
41
 
42
42
 
43
43
  class APIClient:
@@ -67,7 +67,7 @@ def fetch_all_pages(
67
67
  response_payload = request()
68
68
  paginated_response = pagination_model(**response_payload)
69
69
  while not paginated_response.is_last():
70
- logger.info(f"Fetching page number {page_number}")
70
+ logger.debug(f"Fetching page number {page_number}")
71
71
  yield from paginated_response.page_results()
72
72
  next_page_parameters = paginated_response.next_page_parameters()
73
73
  new_request = partial(request, **next_page_parameters)
@@ -79,5 +79,5 @@ def fetch_all_pages(
79
79
  page_number += 1
80
80
 
81
81
  # send last page's results
82
- logger.info(f"Fetching page number {page_number}")
82
+ logger.debug(f"Fetching page number {page_number}")
83
83
  yield from paginated_response.page_results()
@@ -35,7 +35,7 @@ def write_json(filename: str, data: Any):
35
35
  """
36
36
  with open(filename, "w", encoding=ENCODING) as f:
37
37
  json.dump(data, f)
38
- logger.info(f"Wrote output file: {filename}")
38
+ logger.info(f"Wrote output file: {filename} ({f.tell()} bytes)")
39
39
 
40
40
 
41
41
  def _current_version() -> str:
@@ -11,7 +11,7 @@ from ...utils import (
11
11
  write_summary,
12
12
  )
13
13
  from .assets import TableauRevampAsset
14
- from .client import TableauRevampClient
14
+ from .client import TableauRevampClient, TableauRevampCredentials
15
15
 
16
16
  logger = logging.getLogger(__name__)
17
17
 
@@ -26,16 +26,19 @@ def iterate_all_data(
26
26
  yield asset, deep_serialize(data)
27
27
 
28
28
 
29
- def extract_all(client: TableauRevampClient, **kwargs: str) -> None:
29
+ def extract_all(**kwargs) -> None:
30
30
  """
31
- Extract Data from tableau
32
- Store data locally in files under the output_directory
33
- If errors from Tableau's API are catch store them locally in file under the output_directory
31
+ Extract Data From tableau and store it locally in files under the
32
+ output_directory
34
33
  """
35
- output_directory = kwargs.get("output_directory") or from_env(OUTPUT_DIR)
36
-
34
+ output_directory = kwargs.get("output") or from_env(OUTPUT_DIR)
35
+ with_pulse = kwargs.get("with_pulse") or False
37
36
  timestamp = current_timestamp()
38
37
 
38
+ credentials = TableauRevampCredentials(**kwargs)
39
+ client = TableauRevampClient(credentials, with_pulse=with_pulse)
40
+ client.login()
41
+
39
42
  for key, data in iterate_all_data(client):
40
43
  filename = get_output_filename(key.value, output_directory, timestamp)
41
44
  write_json(filename, data)
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  from functools import partial
3
+ from http import HTTPStatus
3
4
  from typing import Iterator, List, Optional, Set, Tuple
4
5
 
5
6
  import requests
@@ -12,6 +13,7 @@ from ...utils import (
12
13
  fetch_all_pages,
13
14
  handle_response,
14
15
  retry,
16
+ retry_request,
15
17
  safe_mode,
16
18
  )
17
19
  from ..abstract import TimeFilter
@@ -135,6 +137,10 @@ class DatabricksAPIClient(APIClient):
135
137
  max_retries=_RETRY_ATTEMPTS,
136
138
  base_ms=_RETRY_BASE_MS,
137
139
  )
140
+ @retry_request(
141
+ status_codes=(HTTPStatus.TOO_MANY_REQUESTS,),
142
+ max_retries=_RETRY_ATTEMPTS,
143
+ )
138
144
  def get_single_column_lineage(
139
145
  self,
140
146
  names: Tuple[str, str],
@@ -15,7 +15,8 @@ from .types import TablesColumns, TimestampedLink
15
15
 
16
16
  logger = logging.getLogger(__name__)
17
17
 
18
- _MAX_THREADS = 10
18
+ _THREADS_COLUMN_LINEAGE = 2
19
+ _THREADS_TABLE_LINEAGE = 10
19
20
 
20
21
 
21
22
  class DatabricksClient:
@@ -99,7 +100,7 @@ class DatabricksClient:
99
100
  Wrapper function that retrieves all table lineage
100
101
  """
101
102
  # retrieve table lineage
102
- with ThreadPoolExecutor(max_workers=_MAX_THREADS) as executor:
103
+ with ThreadPoolExecutor(max_workers=_THREADS_TABLE_LINEAGE) as executor:
103
104
  table_paths = [
104
105
  ".".join([table["schema_id"], table["table_name"]])
105
106
  for table in tables
@@ -121,10 +122,15 @@ class DatabricksClient:
121
122
  candidate_paths = paths_for_column_lineage(
122
123
  tables, columns, table_lineage
123
124
  )
125
+ # retrieve column lineage
126
+ with ThreadPoolExecutor(
127
+ max_workers=_THREADS_COLUMN_LINEAGE
128
+ ) as executor:
129
+ results = executor.map(
130
+ self.api_client.get_single_column_lineage, candidate_paths
131
+ )
124
132
  lineages: List[TimestampedLink] = [
125
- link
126
- for paths in candidate_paths
127
- for link in self.api_client.get_single_column_lineage(paths)
133
+ link for links in results for link in links
128
134
  ]
129
135
  deduplicated = deduplicate_lineage(lineages)
130
136
  return self.formatter.format_lineage(deduplicated)
@@ -12,8 +12,8 @@ _KEYS = ("user", "password", "host", "port", "database")
12
12
 
13
13
 
14
14
  def _check_key(credentials: dict) -> None:
15
- for key in credentials:
16
- if key not in _KEYS:
15
+ for key in _KEYS:
16
+ if key not in credentials:
17
17
  raise KeyError(f"Missing {key} in credentials")
18
18
 
19
19
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: castor-extractor
3
- Version: 0.19.8
3
+ Version: 0.20.0
4
4
  Summary: Extract your metadata assets.
5
5
  Home-page: https://www.castordoc.com/
6
6
  License: EULA
@@ -208,6 +208,14 @@ For any questions or bug report, contact us at [support@castordoc.com](mailto:su
208
208
 
209
209
  # Changelog
210
210
 
211
+ ## 0.20.0 - 2024-09-23
212
+
213
+ * Switch to Tableau revamped connector
214
+
215
+ ## 0.19.9 - 2024-09-19
216
+
217
+ * Databricks: multithreading to retrieve column lineage
218
+
211
219
  ## 0.19.8 - 2024-09-18
212
220
 
213
221
  * Metabase: Handle duplicate dashboards
@@ -1,4 +1,4 @@
1
- CHANGELOG.md,sha256=5uXftRzGolo1ItaqsT8BA9ItULJemWj5yjHHygl-ePg,13456
1
+ CHANGELOG.md,sha256=_iTzy8VrdNYmYKWXfGfPMIlSenr4M7LCvoE1K0H96co,13601
2
2
  Dockerfile,sha256=HcX5z8OpeSvkScQsN-Y7CNMUig_UB6vTMDl7uqzuLGE,303
3
3
  DockerfileUsage.md,sha256=2hkJQF-5JuuzfPZ7IOxgM6QgIQW7l-9oRMFVwyXC4gE,998
4
4
  LICENCE,sha256=sL-IGa4hweyya1HgzMskrRdybbIa2cktzxb5qmUgDg8,8254
@@ -23,7 +23,7 @@ castor_extractor/commands/extract_salesforce_reporting.py,sha256=FdANTNiLkIPdm80
23
23
  castor_extractor/commands/extract_sigma.py,sha256=sxewHcZ1Doq35V2qnpX_zCKKXkrb1_9bYjUMg7BOW-k,643
24
24
  castor_extractor/commands/extract_snowflake.py,sha256=vYiruxRoo--GeMemOGsSE1w9kcKTh_y4E165HtMVzkM,1982
25
25
  castor_extractor/commands/extract_sqlserver.py,sha256=lwhbcNChaXHZgMgSOch3faVr7WJw-sDU6GHl3lzBt_0,1141
26
- castor_extractor/commands/extract_tableau.py,sha256=u-6UCd-kfXwyhNWYxZusqtgTTYkf4gAJS1vRIYWsAVU,1415
26
+ castor_extractor/commands/extract_tableau.py,sha256=VUb_1Y85EzfF1f9OaCQQt8kFYBdp0u31Mw1Wm2fkxWs,1221
27
27
  castor_extractor/commands/file_check.py,sha256=VSD84kpQKf7b0wJOhUgkJQ9n4mK3v52sjMWL7wkNYa0,2667
28
28
  castor_extractor/commands/upload.py,sha256=WLDI3zDmK2CjtbxiMWX2mZGjxx8DozfCw6tLE3CAMcE,1833
29
29
  castor_extractor/file_checker/__init__.py,sha256=OSt6YLhUT42U_Cp3LCLHMVruwDkksL75Ij13X2UPnVk,119
@@ -65,7 +65,7 @@ castor_extractor/uploader/env_test.py,sha256=ClCWWtwd2N-5ClIDUxVMeKkWfhhOTxpppsX
65
65
  castor_extractor/uploader/upload.py,sha256=c86NP4ZxWnz3Hy1iWDYd9qjJSSjZ1bLq3fxVGBIU4Rc,3238
66
66
  castor_extractor/uploader/upload_test.py,sha256=7fwstdQe7FjuwGilsCdFpEQr1qLoR2WTRUzyy93fISw,402
67
67
  castor_extractor/uploader/utils.py,sha256=Tx_i875L2vJ8btOLV3-L0UMEFiyhH8E5n0XXRyLjO0Y,793
68
- castor_extractor/utils/__init__.py,sha256=MReSpH6I4AQZ5WTQp752P2sP4wVVZO8MyyglN0K0VKw,1509
68
+ castor_extractor/utils/__init__.py,sha256=jyYquzC2-R-UYl3VTP49ZDHB0IErGogTPMy3GfScbaA,1524
69
69
  castor_extractor/utils/argument_parser.py,sha256=S4EcIh3wNDjs3fOrQnttCcPsAmG8m_Txl7xvEh0Q37s,283
70
70
  castor_extractor/utils/argument_parser_test.py,sha256=wnyLFJ74iEiPxxLSbwFtckR7FIHxsFOVU38ljs9gqRA,633
71
71
  castor_extractor/utils/client/__init__.py,sha256=h5gm8UNNCCkAqhjYK5f6BY7k0cHFOyAvkmlktqwpir0,392
@@ -73,9 +73,9 @@ castor_extractor/utils/client/abstract.py,sha256=aA5Qcb9TwWDSMq8WpXbGkOB20hehwX2
73
73
  castor_extractor/utils/client/api/__init__.py,sha256=vlG7WXznYgLTn3XyMGsyUkgRkup8FbKM14EXJ8mv-b0,264
74
74
  castor_extractor/utils/client/api/auth.py,sha256=QDLM5h1zGibLaKyATxLF0gycg01SE92G-Y69f_YBClc,1896
75
75
  castor_extractor/utils/client/api/auth_test.py,sha256=NoZYsz7bcCyWBZdMF1TaOuK-s1j09DhTRyM4GSUW_YQ,1311
76
- castor_extractor/utils/client/api/client.py,sha256=h8cQZ4CSD0LUuNv1VC5qa_p_jFbiOUbYztJnNmpJa2Q,4231
76
+ castor_extractor/utils/client/api/client.py,sha256=0E5GG5Yxk-J5B11YdeIcccYk7jAfuQoWJIz5ljMGYUE,4275
77
77
  castor_extractor/utils/client/api/client_test.py,sha256=FM3ZxsLLfMOBn44cXX6FIgnA31-5TTNIyp9D4LBwtXE,1222
78
- castor_extractor/utils/client/api/pagination.py,sha256=Ne-4dqKrwEAETheBn14KPrGRzxsrkuCO96Z6gkCI-0Y,2437
78
+ castor_extractor/utils/client/api/pagination.py,sha256=Efg3P9ct_U5rtgXijMGV05oQxSzjldEopECWjIFWerM,2439
79
79
  castor_extractor/utils/client/api/pagination_test.py,sha256=jCOgXFXrH-jrCxe2dfk80ZksJF-EtmpJPU11BGabsqk,1385
80
80
  castor_extractor/utils/client/api/safe_request.py,sha256=SeBteAK8KhBjXldIdyUpkZphf9ktjzbvBM49AXrvD0g,1686
81
81
  castor_extractor/utils/client/api/safe_request_test.py,sha256=LqS5FBxs6lLLcTkcgxIoLb6OinxShHXR5y4CWZpwmwg,2005
@@ -129,7 +129,7 @@ castor_extractor/utils/time_test.py,sha256=pEwpcHI7wGPnfgwrH1DNHEbPz3HEAryNF5yPL
129
129
  castor_extractor/utils/type.py,sha256=87t32cTctEjX-_BqZLtPLWu-M9OVvw_lFU4DbaQ6V0U,313
130
130
  castor_extractor/utils/validation.py,sha256=NNMkdyvMzConslnyCM3gmciEtPPvefW0vAT2gNsMhvE,1909
131
131
  castor_extractor/utils/validation_test.py,sha256=aSetitOCkH_K-Wto9ISOVGso5jGfTUOBLm3AZnvavO8,1181
132
- castor_extractor/utils/write.py,sha256=CbLMz-mkUFduwogERwe69GXXVO65HEDirVm-kDJkRxg,2135
132
+ castor_extractor/utils/write.py,sha256=_7tNpu2p35E3GZnjsC_GWBbviR3pz3xsL7KAUahs8UE,2154
133
133
  castor_extractor/visualization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
134
134
  castor_extractor/visualization/domo/__init__.py,sha256=1axOCPm4RpdIyUt9LQEvlMvbOPllW8rk63h6EjVgJ0Y,111
135
135
  castor_extractor/visualization/domo/assets.py,sha256=bK1urFR2tnlWkVkkhR32mAKMoKbESNlop-CNGx-65PY,206
@@ -281,7 +281,7 @@ castor_extractor/visualization/tableau_revamp/client/errors.py,sha256=dTe1shqmWm
281
281
  castor_extractor/visualization/tableau_revamp/client/gql_queries.py,sha256=-V3ToD5Gi7nmfVB2OxTOZw8dcOiF7_ciSWjjW2UdvvI,2270
282
282
  castor_extractor/visualization/tableau_revamp/client/rest_fields.py,sha256=gx39X1zMfRVpjmFbgvbgbvtlE0QwxOtk8rZFsIqeGRI,978
283
283
  castor_extractor/visualization/tableau_revamp/constants.py,sha256=thS935pJyuZkdciM2EFHbIuTqSFYfB3YGCJYJ_Ls294,55
284
- castor_extractor/visualization/tableau_revamp/extract.py,sha256=o8ZORM5OXtp51plmDNhk7o0egc0Vf2MOBysjJtRucBI,1289
284
+ castor_extractor/visualization/tableau_revamp/extract.py,sha256=BPy38rFjGG6Nh1eDFeCckE4RHaO-bWW2uhXh7wm8mKk,1368
285
285
  castor_extractor/warehouse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
286
286
  castor_extractor/warehouse/abstract/__init__.py,sha256=Fdfa026tgOo64MvzVRLHM_F2G-JmcehrF0mh3dHgb7s,419
287
287
  castor_extractor/warehouse/abstract/asset.py,sha256=Qs7T2Iw7KHgWVT2aAoBfCQ8tB143cUZY-DRUSkpgvGU,2689
@@ -308,9 +308,9 @@ castor_extractor/warehouse/bigquery/queries/view_ddl.sql,sha256=obCm-IN9V8_YSZTw
308
308
  castor_extractor/warehouse/bigquery/query.py,sha256=5Qc8PEa-kQKpTzguj4RNCAwKyvzWt20vAESYNB0lueo,4768
309
309
  castor_extractor/warehouse/bigquery/types.py,sha256=DHK3wUaaLyLMp7LP-7QkXTDYpYTZiPtvptAOkpxgp4g,88
310
310
  castor_extractor/warehouse/databricks/__init__.py,sha256=YG3YSIJgCFRjjI8eExy9T7qGnfnjWhMFh8c15KTs_BA,184
311
- castor_extractor/warehouse/databricks/api_client.py,sha256=lfS-DMLlTEsEMAEs7TeH8JmLX74l4Ai0k12nuP2fo2Y,8110
311
+ castor_extractor/warehouse/databricks/api_client.py,sha256=z9sIgZ4S3wZzUcVa_mlAW8s70sY8o7v19Kj_r6M0skM,8274
312
312
  castor_extractor/warehouse/databricks/api_client_test.py,sha256=YTWC-X7L-XAfK5b39TUgTmR1ifv0QrY5tvLNoSbpmjg,466
313
- castor_extractor/warehouse/databricks/client.py,sha256=neH-KnyWtxEMApbjIV2uhY7Me3tCpavd1NMnljrT05k,4768
313
+ castor_extractor/warehouse/databricks/client.py,sha256=7MtOvxhxRQOEGi9lmYWbi2W9baNQEDnuF3QTrycdzUw,5004
314
314
  castor_extractor/warehouse/databricks/client_test.py,sha256=UKr_D3M8mhqV1oL2_3y_6pEzAFLVE3FHDNZh4omFLK4,2286
315
315
  castor_extractor/warehouse/databricks/credentials.py,sha256=iphbVynVTQXMEbJy4QaT5fer-GpOi7QtbAlg8R7-Lj4,598
316
316
  castor_extractor/warehouse/databricks/endpoints.py,sha256=qPoL9CtPFJdwVuW9rJ37nmeMd-nChOBouEVYb4SlaUE,670
@@ -392,7 +392,7 @@ castor_extractor/warehouse/snowflake/queries/user.sql,sha256=88V8eRj1NDaD_ufclsK
392
392
  castor_extractor/warehouse/snowflake/queries/view_ddl.sql,sha256=eWsci_50cxiYIv3N7BKkbXVM3RoIzqSDtohqRnE5kg4,673
393
393
  castor_extractor/warehouse/snowflake/query.py,sha256=yDpG4e23xtjEfAKNSAgL9wx17ChFSlvAbig2mJ5ZEC0,1769
394
394
  castor_extractor/warehouse/sqlserver/__init__.py,sha256=PdOuYznmvKAbfWAm8UdN47MfEsd9jqPi_dDi3WEo1KY,116
395
- castor_extractor/warehouse/sqlserver/client.py,sha256=l6O0OhAKtRqy0oTl9KDYDawgWKc4XCNOA-bVdnMdEjM,1592
395
+ castor_extractor/warehouse/sqlserver/client.py,sha256=8OCWD9Xv2M1pUAdWdcvChU5eLFIW565zyC0xmbtrZf0,1592
396
396
  castor_extractor/warehouse/sqlserver/extract.py,sha256=2mBNx9clyrhoiirD635BW-5u6pPoxHyIsB071XoZjho,2087
397
397
  castor_extractor/warehouse/sqlserver/queries/.sqlfluff,sha256=yy0KQdz8I_67vnXyX8eeWwOWkxTXvHyVKSVwhURktd8,48
398
398
  castor_extractor/warehouse/sqlserver/queries/column.sql,sha256=Szdf8hwcDffRTgtD6zf4ZuIyHIVijFgSDk1rZbKI3g8,2480
@@ -402,8 +402,8 @@ castor_extractor/warehouse/sqlserver/queries/table.sql,sha256=kbBQP-TdG5px1IVgyx
402
402
  castor_extractor/warehouse/sqlserver/queries/user.sql,sha256=gOrZsMVypusR2dc4vwVs4E1a-CliRsr_UjnD2EbXs-A,94
403
403
  castor_extractor/warehouse/sqlserver/query.py,sha256=j_d5-HMnzBouwGfywVZMRSSwbXzPvzDWlFCZmvxcoGQ,539
404
404
  castor_extractor/warehouse/synapse/queries/column.sql,sha256=lNcFoIW3Y0PFOqoOzJEXmPvZvfAsY0AP63Mu2LuPzPo,1351
405
- castor_extractor-0.19.8.dist-info/LICENCE,sha256=sL-IGa4hweyya1HgzMskrRdybbIa2cktzxb5qmUgDg8,8254
406
- castor_extractor-0.19.8.dist-info/METADATA,sha256=aTfG24vormzYihCb2vjGUX2_EfOr9rDvmCyRABWhd10,20674
407
- castor_extractor-0.19.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
408
- castor_extractor-0.19.8.dist-info/entry_points.txt,sha256=X_pDYOmhUUMbiAD9h2GZveuGdT8UgL38KJqP44xkvqo,1495
409
- castor_extractor-0.19.8.dist-info/RECORD,,
405
+ castor_extractor-0.20.0.dist-info/LICENCE,sha256=sL-IGa4hweyya1HgzMskrRdybbIa2cktzxb5qmUgDg8,8254
406
+ castor_extractor-0.20.0.dist-info/METADATA,sha256=u6XUIwUCR1lLswRR_mMI9sm9zXrqIbE05MGVnAPLOBA,20819
407
+ castor_extractor-0.20.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
408
+ castor_extractor-0.20.0.dist-info/entry_points.txt,sha256=X_pDYOmhUUMbiAD9h2GZveuGdT8UgL38KJqP44xkvqo,1495
409
+ castor_extractor-0.20.0.dist-info/RECORD,,