cobweb-launcher 0.0.4__py3-none-any.whl → 0.0.6__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.
cobweb/__init__.py CHANGED
@@ -1,10 +1,8 @@
1
- from .base.config import StorerInfo, SchedulerInfo, RedisInfo, SchedulerDB, StorerDB
2
- from .base.interface import StorerInterface, SchedulerInterface
1
+ from .bbb import Seed, Queue, DBItem
2
+ from .task import Task
3
+ from .log import log
4
+ from .interface import SchedulerInterface, StorerInterface
5
+ from .db.redis_db import RedisDB
3
6
  from .distributed.launcher import launcher
4
- from .distributed import models
5
- from .base.task import Task
6
- from .base.bbb import Seed
7
- from .db.base import *
8
- from .db.storer import *
9
- from .db.scheduler import *
7
+
10
8
 
@@ -2,8 +2,8 @@
2
2
  import json
3
3
  import time
4
4
  import hashlib
5
- from log import log
6
- from utils import struct_queue_name
5
+ from .log import log
6
+ from .utils import struct_queue_name
7
7
  from collections import deque, namedtuple
8
8
 
9
9
 
cobweb/db/__init__.py CHANGED
@@ -0,0 +1,2 @@
1
+ from . import oss_db, redis_db
2
+ from . import scheduler, storer
@@ -2,7 +2,7 @@ import oss2
2
2
  from typing import Union
3
3
  from oss2.models import PartInfo
4
4
  from requests import Response
5
- from base.log import log
5
+ from cobweb import log
6
6
 
7
7
 
8
8
  class OssDB:
@@ -1,6 +1,6 @@
1
1
  import time
2
2
  import redis
3
- from base.bbb import Seed
3
+ from cobweb import Seed
4
4
 
5
5
 
6
6
  class RedisDB:
@@ -9,19 +9,9 @@ class RedisDB:
9
9
  self,
10
10
  project: str,
11
11
  task_name: str,
12
- host=None,
13
- port=None,
14
- username=None,
15
- password=None,
16
- db=0
12
+ config: dict
17
13
  ):
18
- pool = redis.ConnectionPool(
19
- host=host,
20
- port=port,
21
- username=username,
22
- password=password,
23
- db=db
24
- )
14
+ pool = redis.ConnectionPool(**config)
25
15
  self.heartbeat_key = f"{project}:{task_name}:heartbeat" # redis type string
26
16
  self.spider_key = f"{project}:{task_name}:seed_info:spider" # redis type zset, .format(priority)
27
17
  self.storer_key = f"{project}:{task_name}:seed_info:storer:%s" # redis type set,
@@ -1,4 +1,4 @@
1
- from base.interface import SchedulerInterface
1
+ from cobweb import SchedulerInterface
2
2
 
3
3
 
4
4
  class Default(SchedulerInterface):
@@ -1,6 +1,4 @@
1
- from base.log import log
2
- from base.bbb import Seed
3
- from base.interface import SchedulerInterface
1
+ from cobweb import log, Seed, SchedulerInterface
4
2
 
5
3
 
6
4
  class Textfile(SchedulerInterface):
@@ -1,5 +1,4 @@
1
- from base.log import log
2
- from base.interface import StorerInterface
1
+ from cobweb import log, StorerInterface
3
2
 
4
3
 
5
4
  class Console(StorerInterface):
@@ -1,13 +1,12 @@
1
1
  import json
2
- from base.log import log
3
- from base.interface import StorerInterface
4
2
  from aliyun.log import LogClient, LogItem, PutLogsRequest
3
+ from cobweb import log, StorerInterface
5
4
 
6
5
 
7
6
  class Loghub(StorerInterface):
8
7
 
9
- def __init__(self, table, fields, length, queue, config):
10
- super().__init__(table, fields, length, queue, config)
8
+ def __init__(self, **kwargs):
9
+ super().__init__(**kwargs)
11
10
  self.client = None
12
11
 
13
12
  def init_loghub_clint(self):
cobweb/db/storer/redis.py CHANGED
@@ -1,5 +1,4 @@
1
- from base.log import log
2
- from base.interface import StorerInterface
1
+ from cobweb import log, StorerInterface
3
2
 
4
3
 
5
4
  class Redis(StorerInterface):
@@ -1,5 +1,4 @@
1
- from base.log import log
2
- from base.interface import StorerInterface
1
+ from cobweb import log, StorerInterface
3
2
 
4
3
 
5
4
  class Textfile(StorerInterface):
@@ -1,34 +1,71 @@
1
1
  import time
2
2
  import threading
3
3
  from threading import Thread
4
- from base.log import log
5
- from db.base.redis_db import RedisDB
6
- from base.bbb import Queue, Seed, DBItem
7
- from base.utils import struct_queue_name, restore_table_name
8
- from models import Scheduler, Spider, Storer
9
-
10
-
11
- def start_seeds(seeds):
12
- if not seeds:
13
- return None
14
- if any(isinstance(seeds, t) for t in (list, tuple)):
15
- return [Seed(seed) for seed in seeds]
16
- elif any(isinstance(seeds, t) for t in (str, dict)):
17
- return Seed(seeds)
18
-
19
-
20
- def parse_storer_info(storer_info):
21
- storer_data = {}
22
- storer_info_list = []
23
- if storer_info.__class__.__name__ == 'StorerInfo':
24
- storer_info_list.append(storer_info)
25
- elif any(isinstance(storer_info, t) for t in (list, tuple)):
26
- storer_info_list = storer_info
27
- for info in storer_info_list:
28
- db_name = info.DB.__name__
29
- storer_data.setdefault(db_name, {"StorerDB": info.DB, "db_args_list": []})
30
- storer_data[db_name]["db_args_list"].append(info[1:])
31
- return storer_data
4
+ from importlib import import_module
5
+
6
+ from cobweb import log, Queue, DBItem, RedisDB, StorerInterface
7
+ from cobweb.utils import struct_queue_name, restore_table_name
8
+ from .models import Scheduler, Spider, Storer
9
+ from collections import namedtuple
10
+
11
+ # def start_seeds(seeds):
12
+ # if not seeds:
13
+ # return None
14
+ # if any(isinstance(seeds, t) for t in (list, tuple)):
15
+ # return [Seed(seed) for seed in seeds]
16
+ # elif any(isinstance(seeds, t) for t in (str, dict)):
17
+ # return Seed(seeds)
18
+
19
+
20
+ # def parse_storer_info(storer_info):
21
+ # storer_data = {}
22
+ # storer_info_list = []
23
+ # if storer_info.__class__.__name__ == 'StorerInfo':
24
+ # storer_info_list.append(storer_info)
25
+ # elif any(isinstance(storer_info, t) for t in (list, tuple)):
26
+ # storer_info_list = storer_info
27
+ # for info in storer_info_list:
28
+ # db_name = info.DB.__name__
29
+ # storer_data.setdefault(db_name, {"StorerDB": info.DB, "db_args_list": []})
30
+ # storer_data[db_name]["db_args_list"].append(info[1:])
31
+ # return storer_data
32
+
33
+ def get_scheduler_db(db):
34
+ if isinstance(db, str):
35
+ if "." in db:
36
+ model_path = db.split(".")
37
+ model = import_module(db)
38
+ obj = getattr(model, db)
39
+ else:
40
+ model = import_module(f"cobweb.db.scheduler.{db.lower()}")
41
+ obj = getattr(model, db.capitalize())
42
+ return obj
43
+ # if db.lower() in dir(StorerDB):
44
+ # return getattr(StorerDB, db)
45
+ # else:
46
+ # pass
47
+ elif issubclass(db, StorerInterface):
48
+ return db
49
+ raise TypeError()
50
+
51
+
52
+ def get_storer_db(db):
53
+ if isinstance(db, str):
54
+ if "." in db:
55
+ model_path = db.split(".")
56
+ model = import_module(db)
57
+ obj = getattr(model, db)
58
+ else:
59
+ model = import_module(f"cobweb.db.storer.{db.lower()}")
60
+ obj = getattr(model, db.capitalize())
61
+ return obj, db.lower()
62
+ # if db.lower() in dir(StorerDB):
63
+ # return getattr(StorerDB, db)
64
+ # else:
65
+ # pass
66
+ elif issubclass(db, StorerInterface):
67
+ return db, db.__name__.lower()
68
+ raise TypeError()
32
69
 
33
70
 
34
71
  def check(stop, last, spider, scheduler, storer_list, ready_seed_length, spider_queue_length):
@@ -84,54 +121,96 @@ def launcher(task):
84
121
  stop = threading.Event()
85
122
 
86
123
  # 初始化redis信息
87
- redis_db = RedisDB(task.project, task.task_name, *task.redis_info)
124
+ redis_db = RedisDB(task.project, task.task_name, task.redis_info)
88
125
 
89
126
  log.info("初始化cobweb!")
90
127
 
91
128
  seed_queue = Queue()
92
129
 
130
+ if task.scheduler_info is None:
131
+ task.scheduler_info = dict()
132
+
93
133
  # 调度器动态继承
94
- SchedulerDB, table, sql, length, size, config = task.scheduler_info
95
- SchedulerTmp = type(SchedulerDB.__name__, (Scheduler, SchedulerDB), {})
134
+ sql = task.scheduler_info.get("sql")
135
+ table = task.scheduler_info.get("table")
136
+ size = task.scheduler_info.get("size")
137
+ scheduler_config = task.scheduler_info.get("config")
138
+ scheduler_db = task.scheduler_info.get("db", "default")
139
+ DB = get_scheduler_db(scheduler_db)
140
+ # SchedulerDB, table, sql, length, size, config = task.scheduler_info
141
+ SchedulerTmp = type(DB.__name__, (Scheduler, DB), {})
96
142
 
97
143
  # 初始化调度器
98
- scheduler = SchedulerTmp(table, sql, length, size, seed_queue, config)
144
+ scheduler = SchedulerTmp(
145
+ table=table, sql=sql, size=size, queue=seed_queue,
146
+ length=task.scheduler_queue_length, config=scheduler_config
147
+ )
99
148
 
100
149
  # 初始化采集器
101
150
  spider = Spider(seed_queue, task.max_retries)
102
151
 
103
152
  # 解析存储器信息
104
- storer_data = parse_storer_info(task.storer_info)
153
+ storer_info_list = task.storer_info
154
+ if not isinstance(storer_info_list, list):
155
+ storer_info_list = [storer_info_list]
105
156
 
106
157
  # new item
107
158
  item = type("Item", (object,), {"redis_client": redis_db})()
108
- for db_name in storer_data.keys():
109
- # 存储器动态继承
110
- StorerDB = storer_data[db_name]["StorerDB"]
159
+
160
+ for storer_info in storer_info_list:
161
+ storer_db = storer_info["db"]
162
+ fields = storer_info["fields"]
163
+ storer_table = storer_info.get("table", "console")
164
+ storer_config = storer_info.get("config")
165
+
166
+ StorerDB, db_name = get_storer_db(storer_db)
111
167
  StorerTmp = type(StorerDB.__name__, (Storer, StorerDB), {})
112
- db_args_list = storer_data[db_name]["db_args_list"]
113
- for storer_db_args in db_args_list:
114
- table, fields, length, config = storer_db_args
115
- if not getattr(item, db_name, None):
116
- instance = type(db_name, (DBItem,), {})
117
- setattr(item, db_name, instance)
118
- # 创建存储xxx, 创建存储队列
119
- storer_item_instance = getattr(item, db_name)
120
- storer_item_instance.init_item(table, fields)
121
- #
122
- storer_queue = struct_queue_name(db_name, table)
123
- queue = getattr(storer_item_instance, storer_queue)
124
- # 初始话存储器
125
- table_name = restore_table_name(table_name=table)
126
- storer = StorerTmp(table_name, fields, length, queue, config)
127
- storer_list.append(storer)
168
+
169
+ if not getattr(item, db_name, None):
170
+ instance = type(db_name, (DBItem,), {})
171
+ setattr(item, db_name, instance)
172
+
173
+ storer_item_instance = getattr(item, db_name)
174
+ storer_item_instance.init_item(storer_table, fields)
175
+
176
+ storer_queue = struct_queue_name(db_name, storer_table)
177
+ queue = getattr(storer_item_instance, storer_queue)
178
+ # 初始话存储器
179
+ table_name = restore_table_name(table_name=storer_table)
180
+ storer = StorerTmp(
181
+ table=table_name, fields=fields,
182
+ length=task.storer_queue_length,
183
+ queue=queue, config=storer_config
184
+ )
185
+ storer_list.append(storer)
186
+
187
+ # for db_name in storer_data.keys():
188
+ # # 存储器动态继承
189
+ # StorerDB = storer_data[db_name]["StorerDB"]
190
+ # StorerTmp = type(StorerDB.__name__, (Storer, StorerDB), {})
191
+ # db_args_list = storer_data[db_name]["db_args_list"]
192
+ # for storer_db_args in db_args_list:
193
+ # table, fields, length, config = storer_db_args
194
+ # if not getattr(item, db_name, None):
195
+ # instance = type(db_name, (DBItem,), {})
196
+ # setattr(item, db_name, instance)
197
+ # # 创建存储xxx, 创建存储队列
198
+ # storer_item_instance = getattr(item, db_name)
199
+ # storer_item_instance.init_item(table, fields)
200
+ # #
201
+ # storer_queue = struct_queue_name(db_name, table)
202
+ # queue = getattr(storer_item_instance, storer_queue)
203
+ # # 初始话存储器
204
+ # table_name = restore_table_name(table_name=table)
205
+ # storer = StorerTmp(table_name, fields, length, queue, config)
206
+ # storer_list.append(storer)
128
207
 
129
208
  Thread(target=redis_db.check_spider_queue, args=(stop, len(storer_list))).start()
130
209
  Thread(target=redis_db.set_heartbeat, args=(stop,)).start()
131
210
 
132
211
  # 推送初始种子
133
- seeds = start_seeds(task.start_seed)
134
- redis_db.add_seed(seeds)
212
+ # seeds = start_seeds(task.start_seed)
213
+ redis_db.add_seed(task.seeds)
135
214
  # 启动调度器, 调度至redis队列
136
215
  Thread(
137
216
  # name="xxxx_schedule_seeds",
@@ -190,5 +269,5 @@ def launcher(task):
190
269
  return decorator
191
270
 
192
271
 
193
-
194
-
272
+ # model = get_storer_db("console")
273
+ # print()
@@ -1,8 +1,6 @@
1
1
  import time
2
2
  from hashlib import md5
3
- from base.log import log
4
- from base.bbb import Queue, Seed
5
- from base.interface import SchedulerInterface, StorerInterface
3
+ from cobweb import log, Queue, Seed, StorerInterface, SchedulerInterface
6
4
  # from pympler import asizeof
7
5
 
8
6
 
@@ -1,15 +1,5 @@
1
- import json
2
1
  from abc import ABC, abstractmethod
3
-
4
-
5
- def parse_config(config):
6
- if not config:
7
- return None
8
- if isinstance(config, str):
9
- return json.loads(config)
10
- if isinstance(config, dict):
11
- return config
12
- raise TypeError("config type is not in [string, dict]!")
2
+ from .utils import parse_info
13
3
 
14
4
 
15
5
  class SchedulerInterface(ABC):
@@ -20,7 +10,7 @@ class SchedulerInterface(ABC):
20
10
  self.length = length
21
11
  self.size = size
22
12
  self.queue = queue
23
- self.config = parse_config(config)
13
+ self.config = parse_info(config)
24
14
  self.stop = False
25
15
 
26
16
  @abstractmethod
@@ -35,7 +25,7 @@ class StorerInterface(ABC):
35
25
  self.fields = fields
36
26
  self.length = length
37
27
  self.queue = queue
38
- self.config = parse_config(config)
28
+ self.config = parse_info(config)
39
29
  # self.redis_db = redis_db
40
30
 
41
31
  @abstractmethod
cobweb/task.py ADDED
@@ -0,0 +1,44 @@
1
+ from .utils import parse_info, struct_start_seeds
2
+
3
+
4
+ class Task:
5
+
6
+ def __init__(
7
+ self,
8
+ seeds=None,
9
+ project=None,
10
+ task_name=None,
11
+ redis_info=None,
12
+ storer_info=None,
13
+ scheduler_info=None,
14
+ spider_num=None,
15
+ max_retries=None,
16
+ storer_queue_length=None,
17
+ scheduler_queue_length=None,
18
+ ):
19
+ """
20
+
21
+ :param seeds:
22
+ :param project:
23
+ :param task_name:
24
+ :param redis_info:
25
+ :param storer_info:
26
+ :param scheduler_info: dict(DB="", table="", size="", config="")
27
+ :param spider_num:
28
+ :param max_retries:
29
+ :param storer_queue_length:
30
+ :param scheduler_queue_length:
31
+ """
32
+ self.seeds = struct_start_seeds(seeds)
33
+ self.project = project or "test"
34
+ self.task_name = task_name or "spider"
35
+
36
+ self.redis_info = parse_info(redis_info)
37
+ self.storer_info = parse_info(storer_info)
38
+ self.scheduler_info = parse_info(scheduler_info)
39
+
40
+ self.spider_num = spider_num or 1
41
+ self.max_retries = max_retries or 5
42
+ self.storer_queue_length = storer_queue_length or 100
43
+ self.scheduler_queue_length = scheduler_queue_length or 100
44
+
cobweb/utils.py ADDED
@@ -0,0 +1,85 @@
1
+ import json
2
+ import sys
3
+ from typing import Iterable
4
+
5
+ # from cobweb import Seed
6
+
7
+
8
+ def struct_table_name(table_name):
9
+ return table_name.replace(".", "__p__").replace(":", "__c__")
10
+
11
+
12
+ def restore_table_name(table_name):
13
+ return table_name.replace("__p__", ".").replace("__c__", ":")
14
+
15
+
16
+ def struct_queue_name(db_name, table_name):
17
+ return sys.intern(f"__{db_name}_{table_name}_queue__")
18
+
19
+
20
+ # class StorerDB:
21
+ #
22
+ # @staticmethod
23
+ # def console(self):
24
+ # from db.storer.console import Console
25
+ # table = struct_table_name(table)
26
+ # return StorerInfo(DB=Console, table=table, length=length, config=None)
27
+ #
28
+ # @staticmethod
29
+ # def textfile(table, length=200):
30
+ # from db.storer.textfile import Textfile
31
+ # table = struct_table_name(table)
32
+ # return StorerInfo(DB=Textfile, table=table, length=length, config=None)
33
+ #
34
+ # @staticmethod
35
+ # def loghub(table, length=200, config=None):
36
+ # from db.storer.loghub import Loghub
37
+ # table = struct_table_name(table)
38
+ # return StorerInfo(DB=Loghub, table=table, length=length, config=config)
39
+
40
+
41
+ def parse_info(info):
42
+ if not info:
43
+ return info
44
+
45
+ if isinstance(info, dict):
46
+ return info
47
+
48
+ if isinstance(info, str):
49
+ return json.loads(info)
50
+
51
+ if isinstance(info, Iterable):
52
+ result = list()
53
+ for ii in info:
54
+ if isinstance(ii, str):
55
+ result.append(json.loads(ii))
56
+ elif isinstance(ii, dict):
57
+ result.append(ii)
58
+ else:
59
+ raise TypeError("must be in [str, dict]")
60
+
61
+ return result
62
+
63
+
64
+ def struct_start_seeds(seeds):
65
+ from .bbb import Seed
66
+ if not seeds:
67
+ return None
68
+ if any(isinstance(seeds, t) for t in (list, tuple)):
69
+ return [Seed(seed) for seed in seeds]
70
+ elif any(isinstance(seeds, t) for t in (str, dict)):
71
+ return Seed(seeds)
72
+
73
+
74
+ # def get_storer_db(db):
75
+ #
76
+ # if isinstance(db, str):
77
+ # model = import_module(f" db.storer.{db.lower()}")
78
+ #
79
+ # # if db.lower() in dir(StorerDB):
80
+ # # return getattr(StorerDB, db)
81
+ # # else:
82
+ # # pass
83
+
84
+
85
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cobweb-launcher
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: spider_hole
5
5
  Home-page: https://github.com/Juannie-PP/cobweb
6
6
  Author: Juannie-PP
@@ -0,0 +1,28 @@
1
+ cobweb/__init__.py,sha256=IKQkcts73m_K-b6TGs_IxkdkncnZRU0O_N0MJct2COI,218
2
+ cobweb/bbb.py,sha256=8croagwF3xVdb1Xq3f5g22WydQ-pHHWWnA50895dH3E,5617
3
+ cobweb/interface.py,sha256=um_k2AAQl1HTOvfUlq914DjkpfZVwt2m1B65EpPKrmE,802
4
+ cobweb/log.py,sha256=Gb3_y4IzTo5pJohTggBCU9rK6-ZN3hgTOHkoXHyN6CU,2384
5
+ cobweb/task.py,sha256=awZWFwON34WAJs08TKPaYTbyRjmoNOCBkLCNf2l9C-Q,1282
6
+ cobweb/utils.py,sha256=tHSj_T1Ct7Y-QdIo5w4hCwkO59RlQiq9yGxUOjojMOg,2158
7
+ cobweb/db/__init__.py,sha256=4m9lqmxZCRbaih3Z3rl_BT0GugMd0dkOIgu_P9aeC84,63
8
+ cobweb/db/oss_db.py,sha256=l-Xbqawg1HJgedz9MumXQrr1jMK6_EePXCis11CZEkE,3810
9
+ cobweb/db/redis_db.py,sha256=keVlFpUT7spfNwZ4g_5teROo_uOsjfDWtR-WvAcqZIE,7415
10
+ cobweb/db/scheduler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ cobweb/db/scheduler/default.py,sha256=OxmFX7OvMEhKEq-NF7A8I9cA4V4qWw5vayS-yIbng0A,114
12
+ cobweb/db/scheduler/textfile.py,sha256=atRDeNT-e5toNvyGsCXAxL1FJi77uSYktdCzH_hXGo8,821
13
+ cobweb/db/storer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ cobweb/db/storer/console.py,sha256=f7yZFo4qTieaB9JxbGfrVAclAb2H_wji82dWoZp7HUw,182
15
+ cobweb/db/storer/loghub.py,sha256=4VqZacXWhidzINHXQu2_-E0HOBRCcc86f6LkKfnXD5I,1731
16
+ cobweb/db/storer/redis.py,sha256=7Q2XEQwBL6X_M1uvxzzuSBt6iw9piKw-_FWKm2INZDQ,412
17
+ cobweb/db/storer/textfile.py,sha256=3mDHMvF6Sh5fn3IHzWQxyTUd45V-zUoH8vY3EoRlMx0,415
18
+ cobweb/distributed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ cobweb/distributed/launcher.py,sha256=WEEfZDdOXpEN5ljrYEGQWzPtDM7XcVczEnDO81yCoWg,9135
20
+ cobweb/distributed/models.py,sha256=zRZfSOiP-OyvTVdI5_KScyf_jZmYmaYbxJvohf7ffDA,4390
21
+ cobweb/single/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ cobweb/single/models.py,sha256=lu8teNWnWcUwZFra8XmqyhzOAf3UyuEztwBr1Ne6pUs,2898
23
+ cobweb/single/nest.py,sha256=mL8q9a5BjtoeUyzXCIVw_vyUsNY8ltbvQpYIIpZEDFU,5012
24
+ cobweb_launcher-0.0.6.dist-info/LICENSE,sha256=z1rxSIGOyzcSb3orZxFPxzx-0C1vTocmswqBNxpKfEk,1063
25
+ cobweb_launcher-0.0.6.dist-info/METADATA,sha256=TDawOhbZk0ugagG8ZitV2rv_3iVjaKDu7S_847R6VH4,1225
26
+ cobweb_launcher-0.0.6.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
27
+ cobweb_launcher-0.0.6.dist-info/top_level.txt,sha256=4GETBGNsKqiCUezmT-mJn7tjhcDlu7nLIV5gGgHBW4I,7
28
+ cobweb_launcher-0.0.6.dist-info/RECORD,,
cobweb/base/__init__.py DELETED
File without changes