scrapely-python 0.1.18__tar.gz → 0.1.19__tar.gz
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.
- {scrapely_python-0.1.18/src/scrapely_python.egg-info → scrapely_python-0.1.19}/PKG-INFO +1 -1
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/pyproject.toml +1 -1
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/__init__.py +1 -1
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/charging.py +54 -6
- {scrapely_python-0.1.18 → scrapely_python-0.1.19/src/scrapely_python.egg-info}/PKG-INFO +1 -1
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_charge_limit_guard.py +76 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/LICENSE +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/LICENSE-APACHE +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/NOTICE +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/README.md +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/setup.cfg +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/_browser_locations.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/actor.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/browser_configuration.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/cdp_retry.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/charging_client.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/configuration.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/crypto/__init__.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/crypto/_secrets.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/errors.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/events/__init__.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/events/_types.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/events/event_manager.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/log.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/patch_browser_pool.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/pricing_types.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/proxy_configuration.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/py.typed +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/request_loaders/__init__.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/request_loaders/_scrapely_request_list.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/solver.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/__init__.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/_file_system/__init__.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/_file_system/_key_value_store_client.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/_file_system/_storage_client.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/_metadata.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/api_client.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/client.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/patch_storage.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/scrapely_dataset_client.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/scrapely_kvs_client.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/scrapely_rq_client.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/scrapely_storage_client.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/smart_scrapely_storage_client.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/types.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/utils.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage_clients/__init__.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storages/__init__.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/types.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/webhook.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely_python.egg-info/SOURCES.txt +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely_python.egg-info/dependency_links.txt +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely_python.egg-info/requires.txt +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely_python.egg-info/top_level.txt +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_api_client_retries.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_apify_parity.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_browser_configuration.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_cdp_retry.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_charge_retries.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_charging_parity.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_charging_pending.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_input_proxy_aliases.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_parity.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_patch_browser_pool.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_proxy_configuration.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_push_idempotency.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_run_control.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_secrets.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_solver.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_storage.py +0 -0
- {scrapely_python-0.1.18 → scrapely_python-0.1.19}/tests/test_terminal_status_message.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "scrapely-python"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.19"
|
|
8
8
|
description = "Scrapely Python SDK - A toolkit for building Actors on the Scrapely platform"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT AND Apache-2.0"
|
|
@@ -240,6 +240,12 @@ class ChargingManager:
|
|
|
240
240
|
self._charged_events: Dict[str, int] = {}
|
|
241
241
|
self._max_total_charge_usd: Optional[Decimal] = None
|
|
242
242
|
self._total_charged_usd: Decimal = Decimal('0.0')
|
|
243
|
+
# Run spend the platform counts against maxTotalChargeUsd that this SDK
|
|
244
|
+
# does not charge itself: compute/proxy/browser usage and server-billed
|
|
245
|
+
# events. Learnt from the charge endpoint's remainingBudgetNd (only ever
|
|
246
|
+
# grows), so the local budget gates on the run's true remaining budget.
|
|
247
|
+
self._platform_other_usd: Decimal = Decimal('0')
|
|
248
|
+
self._last_remaining_budget_nd: Optional[int] = None
|
|
243
249
|
self._charge_lock = RLock()
|
|
244
250
|
self._flush_lock = asyncio.Lock()
|
|
245
251
|
# Serializes budget calculation/reservation with PPE-aware dataset pushes.
|
|
@@ -288,6 +294,27 @@ class ChargingManager:
|
|
|
288
294
|
self._refused_default_dataset_items,
|
|
289
295
|
)
|
|
290
296
|
|
|
297
|
+
def _remaining_budget_usd(self) -> Decimal:
|
|
298
|
+
"""maxTotalChargeUsd minus this SDK's charges and the platform's other run spend."""
|
|
299
|
+
assert self._max_total_charge_usd is not None
|
|
300
|
+
return self._max_total_charge_usd - self._total_charged_usd - self._platform_other_usd
|
|
301
|
+
|
|
302
|
+
def _sync_platform_budget(self) -> None:
|
|
303
|
+
"""Fold the last charge response's remainingBudgetNd into the local budget.
|
|
304
|
+
|
|
305
|
+
Called after the local reservation matches what the platform accepted.
|
|
306
|
+
The platform's remaining budget = max - (our charges + usage + server
|
|
307
|
+
events), so ``other`` = max - remaining - our charges. Monotonic: usage
|
|
308
|
+
never shrinks, and a stale answer must not hand budget back.
|
|
309
|
+
"""
|
|
310
|
+
nd = self._last_remaining_budget_nd
|
|
311
|
+
self._last_remaining_budget_nd = None
|
|
312
|
+
if nd is None or self._max_total_charge_usd is None or nd < 0:
|
|
313
|
+
return
|
|
314
|
+
other = self._max_total_charge_usd - Decimal(nd) / Decimal(1_000_000_000) - self._total_charged_usd
|
|
315
|
+
if other > self._platform_other_usd:
|
|
316
|
+
self._platform_other_usd = other
|
|
317
|
+
|
|
291
318
|
def is_charge_limit_reached(self) -> bool:
|
|
292
319
|
"""Whether this pay-per-event run has used up its spending limit.
|
|
293
320
|
|
|
@@ -307,7 +334,7 @@ class ChargingManager:
|
|
|
307
334
|
prices = [price for price in self._per_event_prices.values() if price > 0]
|
|
308
335
|
if not prices:
|
|
309
336
|
return False
|
|
310
|
-
return self.
|
|
337
|
+
return self._remaining_budget_usd() < min(prices)
|
|
311
338
|
|
|
312
339
|
def _mark_charge_limit_hit(self) -> None:
|
|
313
340
|
if self._pricing_info is not None:
|
|
@@ -795,6 +822,7 @@ class ChargingManager:
|
|
|
795
822
|
idempotency_key=idempotency_key,
|
|
796
823
|
deadline=deadline,
|
|
797
824
|
)
|
|
825
|
+
self._last_remaining_budget_nd = _read_remaining_budget_nd(body)
|
|
798
826
|
return _read_accepted_count(body, count)
|
|
799
827
|
except Exception as e:
|
|
800
828
|
last_error = e
|
|
@@ -957,7 +985,7 @@ class ChargingManager:
|
|
|
957
985
|
# legacy one-event overdraw while the budget is not yet exceeded.
|
|
958
986
|
chargeable_count = count
|
|
959
987
|
if self._max_total_charge_usd is not None and affordability_price > 0:
|
|
960
|
-
remaining = self.
|
|
988
|
+
remaining = self._remaining_budget_usd()
|
|
961
989
|
max_count_by_limit = max(0, int(remaining / affordability_price))
|
|
962
990
|
if count > max_count_by_limit:
|
|
963
991
|
chargeable_count = (
|
|
@@ -1045,6 +1073,15 @@ class ChargingManager:
|
|
|
1045
1073
|
charged_count=chargeable_count,
|
|
1046
1074
|
event_charge_limit_reached=self._is_event_limit_reached(event_name),
|
|
1047
1075
|
)
|
|
1076
|
+
self._sync_platform_budget()
|
|
1077
|
+
if self._max_total_charge_usd is not None and self._platform_other_usd > 0:
|
|
1078
|
+
# Usage may have used up what is left: report it on this result.
|
|
1079
|
+
result = ChargeResult(
|
|
1080
|
+
chargeable_within_limit=self._compute_chargeable(),
|
|
1081
|
+
charged_count=result.charged_count,
|
|
1082
|
+
event_charge_limit_reached=result.event_charge_limit_reached
|
|
1083
|
+
or self._is_event_limit_reached(event_name),
|
|
1084
|
+
)
|
|
1048
1085
|
else:
|
|
1049
1086
|
raise ChargeUncertainError(
|
|
1050
1087
|
f'Charge for event {event_name!r} (count={chargeable_count}) '
|
|
@@ -1103,7 +1140,7 @@ class ChargingManager:
|
|
|
1103
1140
|
if event_price is None or event_price <= 0:
|
|
1104
1141
|
return None
|
|
1105
1142
|
|
|
1106
|
-
remaining = self.
|
|
1143
|
+
remaining = self._remaining_budget_usd()
|
|
1107
1144
|
return max(0, int(remaining / event_price))
|
|
1108
1145
|
|
|
1109
1146
|
def calculate_total_charged_amount(self) -> Decimal:
|
|
@@ -1165,7 +1202,7 @@ class ChargingManager:
|
|
|
1165
1202
|
if self._max_total_charge_usd is None:
|
|
1166
1203
|
return items_count
|
|
1167
1204
|
|
|
1168
|
-
remaining = self.
|
|
1205
|
+
remaining = self._remaining_budget_usd()
|
|
1169
1206
|
max_items = int(remaining / combined_price)
|
|
1170
1207
|
|
|
1171
1208
|
return min(items_count, max(0, max_items))
|
|
@@ -1226,7 +1263,7 @@ class ChargingManager:
|
|
|
1226
1263
|
if self._max_total_charge_usd is None:
|
|
1227
1264
|
return False
|
|
1228
1265
|
|
|
1229
|
-
remaining = self.
|
|
1266
|
+
remaining = self._remaining_budget_usd()
|
|
1230
1267
|
return remaining < event_price
|
|
1231
1268
|
|
|
1232
1269
|
def _compute_chargeable(self) -> Dict[str, Optional[int]]:
|
|
@@ -1264,6 +1301,17 @@ def _is_charge_limit_rejection(err: ChargeRejectedError) -> bool:
|
|
|
1264
1301
|
)
|
|
1265
1302
|
|
|
1266
1303
|
|
|
1304
|
+
def _read_remaining_budget_nd(body: Any) -> Optional[int]:
|
|
1305
|
+
"""remainingBudgetNd from a charge response (platforms that send it), else None."""
|
|
1306
|
+
if not isinstance(body, dict):
|
|
1307
|
+
return None
|
|
1308
|
+
data = body.get('data') if isinstance(body.get('data'), dict) else body
|
|
1309
|
+
raw = data.get('remainingBudgetNd') if isinstance(data, dict) else None
|
|
1310
|
+
if isinstance(raw, (int, float)) and not isinstance(raw, bool) and raw >= 0:
|
|
1311
|
+
return int(raw)
|
|
1312
|
+
return None
|
|
1313
|
+
|
|
1314
|
+
|
|
1267
1315
|
def _read_accepted_count(body: Any, fallback: int) -> int:
|
|
1268
1316
|
if not isinstance(body, dict):
|
|
1269
1317
|
return fallback
|
|
@@ -1653,7 +1701,7 @@ async def push_data_with_charges(
|
|
|
1653
1701
|
)
|
|
1654
1702
|
rows = len(rows_in)
|
|
1655
1703
|
if charging_manager._max_total_charge_usd is not None and row_cost > 0:
|
|
1656
|
-
remaining = charging_manager.
|
|
1704
|
+
remaining = charging_manager._remaining_budget_usd()
|
|
1657
1705
|
rows = min(rows, max(0, int(remaining / row_cost)))
|
|
1658
1706
|
|
|
1659
1707
|
ops: List[tuple] = [] # (event, charged, op_id, uncertain)
|
|
@@ -509,3 +509,79 @@ def test_multi_event_validation() -> None:
|
|
|
509
509
|
for bad in ({}, {'result': 0}, {'result': 1.5}, {'apify-default-dataset-item': 1}, {'': 1}, None):
|
|
510
510
|
with pytest.raises(ValueError):
|
|
511
511
|
_validate_row_charges(bad)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
# ---- platform usage counts against the cap (remainingBudgetNd in charge responses) ----
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
class UsageApi(FakeChargeApi):
|
|
518
|
+
"""Ledger whose cap also covers platform usage, reporting remainingBudgetNd like the server."""
|
|
519
|
+
|
|
520
|
+
def __init__(self, *, price: Decimal, max_usd: Decimal, usage: Decimal, send_remaining: bool = True) -> None:
|
|
521
|
+
super().__init__(price=price, max_usd=max_usd)
|
|
522
|
+
self.usage = usage
|
|
523
|
+
self.send_remaining = send_remaining
|
|
524
|
+
|
|
525
|
+
async def post(self, path: str, json_data: Optional[Dict[str, Any]] = None, **_: Any) -> Dict[str, Any]:
|
|
526
|
+
count = int((json_data or {}).get('count', 1))
|
|
527
|
+
left = self.max_usd - self.spent - self.usage
|
|
528
|
+
affordable = int(left / self.price)
|
|
529
|
+
if affordable < 1:
|
|
530
|
+
raise HttpError(402, 'charge-limit-exceeded')
|
|
531
|
+
accepted = min(count, affordable)
|
|
532
|
+
self.spent += self.price * accepted
|
|
533
|
+
body: Dict[str, Any] = {'acceptedCount': accepted, 'requestedCount': count}
|
|
534
|
+
if self.send_remaining:
|
|
535
|
+
body['remainingBudgetNd'] = int((self.max_usd - self.spent - self.usage) * 1_000_000_000)
|
|
536
|
+
return body
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
@pytest.mark.asyncio
|
|
540
|
+
async def test_platform_usage_shrinks_the_local_budget() -> None:
|
|
541
|
+
api = UsageApi(price=Decimal('0.01'), max_usd=Decimal('0.05'), usage=Decimal('0.03'))
|
|
542
|
+
mgr = _manager(api, max_usd='0.05')
|
|
543
|
+
store = Store()
|
|
544
|
+
|
|
545
|
+
r = await mgr.charge('result', 1)
|
|
546
|
+
assert r.charged_count == 1
|
|
547
|
+
# $0.05 cap - $0.01 charged - $0.03 usage: one more event fits, not four.
|
|
548
|
+
assert mgr.calculate_max_event_charge_count_within_limit('result') == 1
|
|
549
|
+
assert mgr.is_charge_limit_reached() is False
|
|
550
|
+
pushed = await _charged_push(mgr, store, [{'n': n} for n in range(5)])
|
|
551
|
+
assert pushed.charged_count == 1 and pushed.event_charge_limit_reached is True
|
|
552
|
+
assert len(store.items) == 1
|
|
553
|
+
assert mgr.is_charge_limit_reached() is True
|
|
554
|
+
# The prepaid row from the first charge is still saved; nothing beyond it.
|
|
555
|
+
await _plain_push(mgr, store, [{'p': 1}, {'p': 2}])
|
|
556
|
+
assert len(store.items) == 2
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
@pytest.mark.asyncio
|
|
560
|
+
async def test_usage_learnt_from_a_charge_marks_the_limit_on_that_result() -> None:
|
|
561
|
+
api = UsageApi(price=Decimal('0.01'), max_usd=Decimal('0.05'), usage=Decimal('0.04'))
|
|
562
|
+
mgr = _manager(api, max_usd='0.05')
|
|
563
|
+
r = await mgr.charge('result', 1)
|
|
564
|
+
assert r.charged_count == 1
|
|
565
|
+
assert r.event_charge_limit_reached is True
|
|
566
|
+
assert mgr.is_charge_limit_reached() is True
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
@pytest.mark.asyncio
|
|
570
|
+
async def test_without_remaining_budget_in_responses_behaviour_is_unchanged() -> None:
|
|
571
|
+
api = UsageApi(price=Decimal('0.01'), max_usd=Decimal('0.05'), usage=Decimal('0.03'), send_remaining=False)
|
|
572
|
+
mgr = _manager(api, max_usd='0.05')
|
|
573
|
+
await mgr.charge('result', 1)
|
|
574
|
+
assert mgr.calculate_max_event_charge_count_within_limit('result') == 4
|
|
575
|
+
assert mgr._platform_other_usd == 0
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
@pytest.mark.asyncio
|
|
579
|
+
async def test_platform_other_spend_never_shrinks() -> None:
|
|
580
|
+
api = UsageApi(price=Decimal('0.01'), max_usd=Decimal('1'), usage=Decimal('0.30'))
|
|
581
|
+
mgr = _manager(api, max_usd='1')
|
|
582
|
+
await mgr.charge('result', 1)
|
|
583
|
+
seen = mgr._platform_other_usd
|
|
584
|
+
assert seen == Decimal('0.30')
|
|
585
|
+
api.usage = Decimal('0.10') # a stale or lower report
|
|
586
|
+
await mgr.charge('result', 1)
|
|
587
|
+
assert mgr._platform_other_usd == seen
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/_file_system/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/scrapely_dataset_client.py
RENAMED
|
File without changes
|
{scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/scrapely_kvs_client.py
RENAMED
|
File without changes
|
{scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/scrapely_rq_client.py
RENAMED
|
File without changes
|
{scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely/storage/scrapely_storage_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{scrapely_python-0.1.18 → scrapely_python-0.1.19}/src/scrapely_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|