diff --git a/changedetectionio/flask_app.py b/changedetectionio/flask_app.py index ae611caa3..3584604de 100644 --- a/changedetectionio/flask_app.py +++ b/changedetectionio/flask_app.py @@ -801,7 +801,7 @@ def changedetection_app(config=None, datastore_o=None): # Recast it if need be to right data Watch handler watch_class = get_custom_watch_obj_for_processor(form.data.get('processor')) - datastore.data['watching'][uuid] = watch_class(datastore_path=datastore_o.datastore_path, default=datastore.data['watching'][uuid]) + datastore.data['watching'][uuid] = watch_class(__datastore=datastore_o, default=datastore.data['watching'][uuid]) flash("Updated watch - unpaused!" if request.args.get('unpause_on_save') else "Updated watch.") # Re #286 - We wait for syncing new data to disk in another thread every 60 seconds diff --git a/changedetectionio/model/App.py b/changedetectionio/model/App.py index b70a7ff00..1204de3e9 100644 --- a/changedetectionio/model/App.py +++ b/changedetectionio/model/App.py @@ -10,7 +10,7 @@ _FILTER_FAILURE_THRESHOLD_ATTEMPTS_DEFAULT = 6 DEFAULT_SETTINGS_HEADERS_USERAGENT='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36' class model(dict): - base_config = { + __base_config = { 'note': "Hello! If you change this file manually, please be sure to restart your changedetection.io instance!", 'watching': {}, 'settings': { @@ -60,7 +60,7 @@ class model(dict): def __init__(self, *arg, **kw): super(model, self).__init__(*arg, **kw) - self.update(self.base_config) + self.update(self.__base_config) def parse_headers_from_text_file(filepath): diff --git a/changedetectionio/model/Watch.py b/changedetectionio/model/Watch.py index f86ac8b69..95ec96bee 100644 --- a/changedetectionio/model/Watch.py +++ b/changedetectionio/model/Watch.py @@ -33,14 +33,17 @@ def is_safe_url(test_url): class model(WatchBase): - __newest_history_key = None + __datastore = None __history_n = 0 + __newest_history_key = None jitter_seconds = 0 - + def __init__(self, *arg, **kw): - self.__datastore_path = kw.get('datastore_path') - if kw.get('datastore_path'): - del kw['datastore_path'] + if not kw.get('__datastore'): + logger.critical('No __datastore reference was set!') + + self.__datastore = kw.get('__datastore') + super(model, self).__init__(*arg, **kw) if kw.get('default'): self.update(kw['default']) @@ -419,7 +422,7 @@ class model(WatchBase): @property def watch_data_dir(self): # The base dir of the watch data - return os.path.join(self.__datastore_path, self['uuid']) if self.__datastore_path else None + return os.path.join(self.__datastore.datastore_path, self['uuid']) if self.__datastore.datastore_path else None def get_error_text(self): """Return the text saved from a previous request that resulted in a non-200 error""" diff --git a/changedetectionio/model/__init__.py b/changedetectionio/model/__init__.py index 475360508..d696b357d 100644 --- a/changedetectionio/model/__init__.py +++ b/changedetectionio/model/__init__.py @@ -7,7 +7,7 @@ from changedetectionio.notification import default_notification_format_for_watch class WatchBase(MutableMapping): def __init__(self, *args, **kwargs): - self.internal_dict = { + self.__internal_dict = { # Custom notification content # Re #110, so then if this is set to None, we know to use the default value instead # Requires setting to None on submit if it's the same as the default @@ -137,42 +137,26 @@ class WatchBase(MutableMapping): # Implement abstract methods required by MutableMapping def __getitem__(self, key): - return self.internal_dict[key] + return self.__internal_dict[key] def __setitem__(self, key, value): - self.internal_dict[key] = value + if key == '__datastore': + self.__datastore = value + else: + self.__internal_dict[key] = value def __delitem__(self, key): - del self.internal_dict[key] + del self.__internal_dict[key] def __iter__(self): - return iter(self.internal_dict) + return iter(self.__internal_dict) def __len__(self): - return len(self.internal_dict) + return len(self.__internal_dict) # Optional: Implement additional methods for convenience def __repr__(self): - return f"{self.__class__.__name__}({self.internal_dict})" + return f"{self.__class__.__name__}({self.__internal_dict})" def as_dict(self): - return self.internal_dict - -# def __getattr__(self, attr): - # Allow attribute-style access -# try: -# return self.internal_dict[attr] -# except KeyError: -# raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attr}'") - -# def __setattr__(self, attr, value): -# if attr == 'internal_dict': -# super().__setattr__(attr, value) -# else: -# self.internal_dict[attr] = value - -# def __delattr__(self, attr): -# if attr == 'internal_dict': -# super().__delattr__(attr) -# else: -# del self.internal_dict[attr] + return self.__internal_dict diff --git a/changedetectionio/store.py b/changedetectionio/store.py index 38571f650..5b589a169 100644 --- a/changedetectionio/store.py +++ b/changedetectionio/store.py @@ -28,8 +28,8 @@ dictfilt = lambda x, y: dict([ (i,x[i]) for i in x if i in set(y) ]) class CustomEncoder(json.JSONEncoder): def default(self, obj): - if isinstance(obj, WatchBase): - return obj.internal_dict + if obj and isinstance(obj, WatchBase): + return obj.as_dict() # Add more custom type handlers here return super().default(obj) @@ -56,9 +56,6 @@ class ChangeDetectionStore: self.needs_write = False self.start_time = time.time() self.stop_thread = False - # Base definition for all watchers - # deepcopy part of #569 - not sure why its needed exactly - self.generic_definition = deepcopy(Watch.model(datastore_path = datastore_path, default={})) if path.isfile('changedetectionio/source.txt'): with open('changedetectionio/source.txt') as f: @@ -165,7 +162,7 @@ class ChangeDetectionStore: if entity.get('uuid') != 'text_json_diff': logger.trace(f"Loading Watch object '{watch_class.__module__}.{watch_class.__name__}' for UUID {uuid}") - entity = watch_class(datastore_path=self.datastore_path, default=entity) + entity = watch_class(__datastore=self, default=entity) return entity def set_last_viewed(self, uuid, timestamp): @@ -184,13 +181,15 @@ class ChangeDetectionStore: return with self.lock: + # deepcopy part of #569 - not sure why its needed exactly +# self.generic_definition = deepcopy(Watch.model(default={})) - # In python 3.9 we have the |= dict operator, but that still will lose data on nested structures... - for dict_key, d in self.generic_definition.items(): - if isinstance(d, dict): - if update_obj is not None and dict_key in update_obj: - self.__data['watching'][uuid][dict_key].update(update_obj[dict_key]) - del (update_obj[dict_key]) +# # In python 3.9 we have the |= dict operator, but that still will lose data on nested structures... +# for dict_key, d in self.generic_definition.items(): +# if isinstance(d, dict): +# if update_obj is not None and dict_key in update_obj: +# self.__data['watching'][uuid][dict_key].update(update_obj[dict_key]) +# del (update_obj[dict_key]) self.__data['watching'][uuid].update(update_obj) self.needs_write = True @@ -353,7 +352,7 @@ class ChangeDetectionStore: # If the processor also has its own Watch implementation watch_class = get_custom_watch_obj_for_processor(apply_extras.get('processor')) - new_watch = watch_class(datastore_path=self.datastore_path, url=url) + new_watch = watch_class(__datastore=self, url=url) new_uuid = new_watch.get('uuid')