Skip to content

Commit fb7eac3

Browse files
committed
Introduction of PyFunceble v4.0.2 (Blue Duckling: Grandiflora)!
Fixed: * Configuration Upgrade issue. * DNS Resolver: issue when an incorrect URL (not sub-domain) is provided. * URL Testing: History may be empty - due to weird server configuration. New: * Introduction of the `fc2.com` SPECIAL rule. Improvement: * Better handling of `ascii` default encoding - around the CLI. Maintenance: * Update outdated URLs. * Fix typo. * Introduction of an internal way to profile memory usage. * DRY around requester and extra rules. * Fix failing tests. Contributors: * @DandelionSprout * @mitchellkrogza * @spirillen * @T145 * @ZeroDot1
2 parents 44b9390 + 8295467 commit fb7eac3

File tree

44 files changed

+2612
-122
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2612
-122
lines changed

PyFunceble/checker/availability/base.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
limitations under the License.
5151
"""
5252

53+
# pylint: disable=too-many-lines
54+
5355
import multiprocessing
5456
from datetime import datetime
5557
from typing import Dict, List, Optional
@@ -60,6 +62,7 @@
6062
import PyFunceble.facility
6163
import PyFunceble.factory
6264
import PyFunceble.storage
65+
from PyFunceble.checker.availability.extra_rules import ExtraRulesHandler
6366
from PyFunceble.checker.availability.params import AvailabilityCheckerParams
6467
from PyFunceble.checker.availability.status import AvailabilityCheckerStatus
6568
from PyFunceble.checker.base import CheckerBase
@@ -123,6 +126,7 @@ class AvailabilityCheckerBase(CheckerBase):
123126
domain_syntax_checker: Optional[DomainSyntaxChecker] = None
124127
ip_syntax_checker: Optional[IPSyntaxChecker] = None
125128
url_syntax_checker: Optional[URLSyntaxChecker] = None
129+
extra_rules_handler: Optional[ExtraRulesHandler] = None
126130

127131
_use_extra_rules: bool = False
128132
_use_whois_lookup: bool = False
@@ -148,15 +152,17 @@ def __init__(
148152
do_syntax_check_first: Optional[bool] = None,
149153
db_session: Optional[Session] = None,
150154
use_whois_db: Optional[bool] = None,
155+
use_collection: Optional[bool] = None,
151156
) -> None:
152-
self.dns_query_tool = DNSQueryTool().guess_all_settings()
157+
self.dns_query_tool = DNSQueryTool()
153158
self.whois_query_tool = WhoisQueryTool()
154159
self.addressinfo_query_tool = AddressInfo()
155160
self.hostbyaddr_query_tool = HostByAddrInfo()
156161
self.http_status_code_query_tool = HTTPStatusCode()
157162
self.domain_syntax_checker = DomainSyntaxChecker()
158163
self.ip_syntax_checker = IPSyntaxChecker()
159164
self.url_syntax_checker = URLSyntaxChecker()
165+
self.extra_rules_handler = ExtraRulesHandler()
160166
self.db_session = db_session
161167

162168
self.params = AvailabilityCheckerParams()
@@ -202,7 +208,10 @@ def __init__(
202208
self.guess_and_set_use_whois_db()
203209

204210
super().__init__(
205-
subject, do_syntax_check_first=do_syntax_check_first, db_session=db_session
211+
subject,
212+
do_syntax_check_first=do_syntax_check_first,
213+
db_session=db_session,
214+
use_collection=use_collection,
206215
)
207216

208217
@property
@@ -940,6 +949,48 @@ def try_to_query_status_from_reputation(self) -> "AvailabilityCheckerBase":
940949

941950
raise NotImplementedError()
942951

952+
def try_to_query_status_from_collection(self) -> "AvailabilityCheckerBase":
953+
"""
954+
Tries to get and set the status from the Collection API.
955+
"""
956+
957+
PyFunceble.facility.Logger.info(
958+
"Started to try to query the status of %r from: Collection Lookup",
959+
self.status.idna_subject,
960+
)
961+
962+
data = self.collection_query_tool.pull(self.idna_subject)
963+
964+
if data and "status" in data:
965+
if (
966+
self.collection_query_tool.preferred_status_origin == "frequent"
967+
and data["status"]["availability"]["frequent"]
968+
):
969+
self.status.status = data["status"]["availability"]["frequent"]
970+
self.status.status_source = "COLLECTION"
971+
elif (
972+
self.collection_query_tool.preferred_status_origin == "latest"
973+
and data["status"]["availability"]["latest"]
974+
):
975+
self.status.status = data["status"]["availability"]["latest"]["status"]
976+
self.status.status_source = "COLLECTION"
977+
elif (
978+
self.collection_query_tool.preferred_status_origin == "recommended"
979+
and data["status"]["availability"]["recommended"]
980+
):
981+
self.status.status = data["status"]["availability"]["recommended"]
982+
self.status.status_source = "COLLECTION"
983+
984+
PyFunceble.facility.Logger.info(
985+
"Could define the status of %r from: Collection Lookup",
986+
self.status.idna_subject,
987+
)
988+
989+
PyFunceble.facility.Logger.info(
990+
"Finished to try to query the status of %r from: Collection Lookup",
991+
self.status.idna_subject,
992+
)
993+
943994
@CheckerBase.ensure_subject_is_given
944995
@CheckerBase.update_status_date_after_query
945996
def query_status(self) -> "AvailabilityCheckerBase":

PyFunceble/checker/availability/domain.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
import PyFunceble.factory
5656
import PyFunceble.storage
5757
from PyFunceble.checker.availability.base import AvailabilityCheckerBase
58-
from PyFunceble.checker.availability.extra_rules import ExtraRulesHandler
5958
from PyFunceble.checker.reputation.domain import DomainReputationChecker
6059

6160

@@ -131,6 +130,9 @@ def query_status(
131130

132131
status_post_syntax_checker = None
133132

133+
if not self.status.status and self.use_collection:
134+
self.try_to_query_status_from_collection()
135+
134136
if not self.status.status and self.do_syntax_check_first:
135137
self.try_to_query_status_from_syntax_lookup()
136138

@@ -176,7 +178,7 @@ def query_status(
176178
)
177179

178180
if self.use_extra_rules:
179-
ExtraRulesHandler(self.status).start()
181+
self.extra_rules_handler.set_status(self.status).start()
180182

181183
return self
182184

PyFunceble/checker/availability/extra_rules.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class ExtraRulesHandler:
7979

8080
http_codes_dataset: Optional[Box] = None
8181

82-
def __init__(self, status: Optional[AvailabilityCheckerStatus]) -> None:
82+
def __init__(self, status: Optional[AvailabilityCheckerStatus] = None) -> None:
8383
self.regex_active2inactive = {
8484
r"\.000webhostapp\.com": [
8585
(self.__switch_to_down_if, 410),
@@ -88,6 +88,7 @@ def __init__(self, status: Optional[AvailabilityCheckerStatus]) -> None:
8888
r"\.angelfire\.com$": [(self.__switch_to_down_if, 404)],
8989
r"\.blogspot\.": [self.__handle_blogspot],
9090
r"\.canalblog\.com$": [(self.__switch_to_down_if, 404)],
91+
r"\.fc2\.com$": [self.__handle_fc2_dot_com],
9192
r"\.github\.io$": [(self.__switch_to_down_if, 404)],
9293
r"\.godaddysites\.com$": [(self.__switch_to_down_if, 404)],
9394
r"\.hpg.com.br$": [(self.__switch_to_down_if, 404)],
@@ -294,6 +295,26 @@ def __handle_wordpress_dot_com(self) -> "ExtraRulesHandler":
294295

295296
return self
296297

298+
def __handle_fc2_dot_com(self) -> "ExtraRulesHandler":
299+
"""
300+
Handles the :code:`fc2.com` case.
301+
302+
.. warning::
303+
This method assume that we know that we are handling a fc2 domain.
304+
"""
305+
306+
if self.status.subject.startswith("http:"):
307+
url = self.status.subject
308+
else:
309+
url = f"http://{self.status.subject}:80"
310+
311+
req = PyFunceble.factory.Requester.get(url, allow_redirects=False)
312+
313+
if "Location" in req.headers and "error.fc2.com" in req.headers["Location"]:
314+
self.__switch_to_down()
315+
316+
return self
317+
297318
def __handle_active2inactive(self) -> "ExtraRulesHandler":
298319
"""
299320
Handles the status deescalation.

PyFunceble/checker/availability/ip.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
import PyFunceble.factory
5656
import PyFunceble.storage
5757
from PyFunceble.checker.availability.base import AvailabilityCheckerBase
58-
from PyFunceble.checker.availability.extra_rules import ExtraRulesHandler
5958
from PyFunceble.checker.reputation.ip import IPReputationChecker
6059

6160

@@ -171,7 +170,7 @@ def query_status(
171170
)
172171

173172
if self.use_extra_rules:
174-
ExtraRulesHandler(self.status).start()
173+
self.extra_rules_handler.set_status(self.status).start()
175174

176175
return self
177176

PyFunceble/checker/base.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,11 @@
5757
import domain2idna
5858
from sqlalchemy.orm import Session
5959

60+
import PyFunceble.facility
61+
import PyFunceble.storage
6062
from PyFunceble.checker.params_base import CheckerParamsBase
6163
from PyFunceble.checker.status_base import CheckerStatusBase
64+
from PyFunceble.query.collection import CollectionQueryTool
6265

6366

6467
class CheckerBase:
@@ -75,13 +78,16 @@ class CheckerBase:
7578
"""
7679

7780
STD_DO_SYNTAX_CHECK_FIRST: bool = False
81+
STD_USE_COLLECTION: bool = False
7882

7983
_do_syntax_check_first: bool = False
84+
_use_collection: bool = False
8085

8186
_subject: Optional[str] = None
8287
_idna_subject: Optional[str] = None
8388

8489
db_session: Optional[Session] = None
90+
collection_query_tool: Optional[CollectionQueryTool] = None
8591

8692
status: Optional[CheckerStatusBase] = None
8793
params: Optional[CheckerParamsBase] = None
@@ -92,7 +98,10 @@ def __init__(
9298
*,
9399
do_syntax_check_first: Optional[bool] = None,
94100
db_session: Optional[Session] = None,
101+
use_collection: Optional[bool] = None,
95102
) -> None:
103+
self.collection_query_tool = CollectionQueryTool()
104+
96105
if self.params is None:
97106
self.params = CheckerParamsBase()
98107

@@ -107,6 +116,11 @@ def __init__(
107116
else:
108117
self.do_syntax_check_first = self.STD_DO_SYNTAX_CHECK_FIRST
109118

119+
if use_collection is not None:
120+
self.use_collection = use_collection
121+
else:
122+
self.guess_and_set_use_collection()
123+
110124
self.db_session = db_session
111125

112126
def propagate_subject(func): # pylint: disable=no-self-argument
@@ -301,6 +315,56 @@ def set_do_syntax_check_first(self, value: bool) -> "CheckerBase":
301315

302316
return self
303317

318+
@property
319+
def use_collection(self) -> bool:
320+
"""
321+
Provides the current value of the :code:`_use_collection` attribute.
322+
"""
323+
324+
return self._use_collection
325+
326+
@use_collection.setter
327+
def use_collection(self, value: bool) -> None:
328+
"""
329+
Sets the value which authorizes the usage of the Collection.
330+
331+
:param value:
332+
The value to set.
333+
334+
:param TypeError:
335+
When the given :code:`value` is not a :py:class:`bool`.
336+
"""
337+
338+
if not isinstance(value, bool):
339+
raise TypeError(f"<value> should be {bool}, {type(value)} given.")
340+
341+
self._use_collection = self.params.use_collection = value
342+
343+
def set_use_collection(self, value: bool) -> "CheckerBase":
344+
"""
345+
Sets the value which authorizes the usage of the Collection.
346+
347+
:param value:
348+
The value to set.
349+
"""
350+
351+
self.use_collection = value
352+
353+
return self
354+
355+
def guess_and_set_use_collection(self) -> "CheckerBase":
356+
"""
357+
Try to guess and set the value of the :code:`use_collection` attribute.
358+
"""
359+
360+
if PyFunceble.facility.ConfigLoader.is_already_loaded():
361+
if isinstance(PyFunceble.storage.CONFIGURATION.lookup.collection, bool):
362+
self.use_collection = PyFunceble.storage.CONFIGURATION.lookup.collection
363+
else:
364+
self.use_collection = self.STD_USE_COLLECTION
365+
else:
366+
self.use_collection = self.STD_USE_COLLECTION
367+
304368
def subject_propagator(self) -> "CheckerBase":
305369
"""
306370
Propagate the currently set subject.

PyFunceble/checker/params_base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class CheckerParamsBase:
6464
"""
6565

6666
do_syntax_check_first: Optional[bool] = None
67+
use_collection: Optional[bool] = None
6768

6869
def to_dict(self) -> dict:
6970
"""

PyFunceble/checker/reputation/base.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,9 @@ def __init__(
9393
subject: Optional[str] = None,
9494
do_syntax_check_first: Optional[bool] = None,
9595
db_session: Optional[Session] = None,
96+
use_collection: Optional[bool] = None,
9697
) -> None:
97-
self.dns_query_tool = DNSQueryTool().guess_all_settings()
98+
self.dns_query_tool = DNSQueryTool()
9899
self.ipv4_reputation_query_tool = IPV4ReputationDataset()
99100
self.domain_syntax_checker = DomainSyntaxChecker()
100101
self.ip_syntax_checker = IPSyntaxChecker()
@@ -107,7 +108,10 @@ def __init__(
107108
self.status.dns_lookup_record = self.dns_query_tool.lookup_record
108109

109110
super().__init__(
110-
subject, do_syntax_check_first=do_syntax_check_first, db_session=db_session
111+
subject,
112+
do_syntax_check_first=do_syntax_check_first,
113+
db_session=db_session,
114+
use_collection=use_collection,
111115
)
112116

113117
@staticmethod
@@ -244,6 +248,48 @@ def try_to_query_status_from_syntax_lookup(self) -> "ReputationCheckerBase":
244248

245249
return self
246250

251+
def try_to_query_status_from_collection(self) -> "ReputationCheckerBase":
252+
"""
253+
Tries to get and set the status from the Collection API.
254+
"""
255+
256+
PyFunceble.facility.Logger.info(
257+
"Started to try to query the status of %r from: Collection Lookup",
258+
self.status.idna_subject,
259+
)
260+
261+
data = self.collection_query_tool[self.idna_subject]
262+
263+
if data and "status" in data:
264+
if (
265+
self.collection_query_tool.preferred_status_origin == "frequent"
266+
and data["status"]["reputation"]["frequent"]
267+
):
268+
self.status.status = data["status"]["reputation"]["frequent"]
269+
self.status.status_source = "COLLECTION"
270+
elif (
271+
self.collection_query_tool.preferred_status_origin == "latest"
272+
and data["status"]["reputation"]["latest"]
273+
):
274+
self.status.status = data["status"]["reputation"]["latest"]["status"]
275+
self.status.status_source = "COLLECTION"
276+
elif (
277+
self.collection_query_tool.preferred_status_origin == "recommended"
278+
and data["status"]["reputation"]["recommended"]
279+
):
280+
self.status.status = data["status"]["reputation"]["recommended"]
281+
self.status.status_source = "COLLECTION"
282+
283+
PyFunceble.facility.Logger.info(
284+
"Could define the status of %r from: Collection Lookup",
285+
self.status.idna_subject,
286+
)
287+
288+
PyFunceble.facility.Logger.info(
289+
"Finished to try to query the status of %r from: Collection Lookup",
290+
self.status.idna_subject,
291+
)
292+
247293
@CheckerBase.ensure_subject_is_given
248294
@CheckerBase.update_status_date_after_query
249295
def query_status(self) -> "ReputationCheckerBase":
@@ -253,6 +299,11 @@ def query_status(self) -> "ReputationCheckerBase":
253299

254300
status_post_syntax_checker = None
255301

302+
if (
303+
not self.status.status and self.use_collection
304+
): # pragma: no cover ## Underlying tested
305+
self.try_to_query_status_from_collection()
306+
256307
if not self.status.status and self.do_syntax_check_first:
257308
self.try_to_query_status_from_syntax_lookup()
258309

0 commit comments

Comments
 (0)