Index
objects of diskcahe
have the property _cache
with Cache
object inside it. Cache
is created with arguments from those in Index
. Unfortunately, it takes into account not all arguments, some needed for me is amongst them. I had two choices: either editing the package that will decrease universality of my code or using the method fromcache
wherein I can put Cache
with necessary properties. I chose the latter.
I would like to add some attributes & methods to object from Index.fromcache()
, & to make its type GeoCache
. I made the next class for this aim:
from __future__ import annotations
from typing import *
from collections.abc import *
import re
import geocoder
from geocoder.arcgis import ArcgisResult
from geocoder.yandex import YandexResult
from diskcache import Index, Cache
class GeoCache(Index):
CYRILLIC_LETTERS_PATTERN = re.compile(r"[А-Я]")
def __new__(cls, *args, **kwargs):
return Index.fromcache(Cache(*args, **kwargs))
def __init__(self, *args, **kwargs):
try:
self.request_map = self.cache["request-address"]
except KeyError:
self.request_map = {}
def __getitem__(self, key):
try:
return super().__getitem__(self.request_map[key])
except KeyError:
query = self.getGeocodeData(key).current_result
self.request_map[key] = query.address
if query.address not in self.request_map.values():
self[query.address] = query
return query
@classmethod
def getGeocodeData(cls, address: str) -> geocoder.api.ArcgisQuery | geocoder.api.YandexQuery:
n = 10
def call() -> geocoder.api.ArcgisQuery | geocoder.api.YandexQuery:
try:
if "Russia" in address:
return geocoder.yandex(location=address, key=YANDEX_APIKEY, lang="en_RU")
if re.search(cls.CYRILLIC_LETTERS_PATTERN, address):
return geocoder.yandex(location=address, key=YANDEX_APIKEY, lang="ru_RU")
return geocoder.arcgis(location=address)
except:
raise Exception(address)
for _ in range(n):
response = call()
if response.ok:
return response
raise Exception(f"I got error {n} times in row with {address}")
def close(self) -> NoReturn:
self.cache["request-address"] = self.request_map
super().cache.close()
But it produces only Index
object. For instance, calling close
gives AttributeError: 'Index' object has no attribute 'close'
. Why does object not take class methods & attributes after __new__
?
The example of using:
geocache = GeoCache(GEOCACHE_PATH, size_limit=10*(1<<30)) #Creates the cache in specified directory & with necessary size limit
geocache["п. Костино, Рыбновский р-н, Рязанская обл"] #Geocode a request, cache & return the result
geocache["п. Костино, Рыбновский р-н, Рязанская обл"] #Return the result from the cache
geocache.close() #Save map "request : real address" & close the cache
CodePudding user response:
So, the problem is fundamentally that your GeoCache.__new__
doesn't return a GeoCache
instance, it returns an Index
instance.
geocache = GeoCache(GEOCACHE_PATH, size_limit=10*(1<<30))
print(isinstance(geocache, GeoCache)
will print False
.
And of course, instances of parent classes do not have access to child-class namespaces. You wouldn't expect index = Index(whatever)
to be able to access a method you only defined in a subclass, would you?
Furthermore, for the instance attributes, __init__
is called only if __new__
returns an instance of that class.
One hack you can do to work around this is just "fix" the type of your instance by changing to to GeoCache
, so:
class GeoCache(Index):
...
def __new__(cls, *args, **kwargs):
instance = Index.fromcache(Cache(*args, **kwargs))
instance.__class__ = GeoCache
return instance
Note, if Index
is a built-in class or defined as a C-extension, then this probably won't work. If it is defined in python, then it could work.