• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

funilrys / PyFunceble / 17046250819

18 Aug 2025 04:12PM UTC coverage: 96.648% (+1.9%) from 94.721%
17046250819

push

github

funilrys
Bump verstion to v4.3.0

1 of 1 new or added line in 1 file covered. (100.0%)

154 existing lines in 14 files now uncovered.

11967 of 12382 relevant lines covered (96.65%)

8.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.32
/PyFunceble/dataset/user_agent.py
1
"""
2
The tool to check the availability or syntax of domain, IP or URL.
3

4
::
5

6

7
    ██████╗ ██╗   ██╗███████╗██╗   ██╗███╗   ██╗ ██████╗███████╗██████╗ ██╗     ███████╗
8
    ██╔══██╗╚██╗ ██╔╝██╔════╝██║   ██║████╗  ██║██╔════╝██╔════╝██╔══██╗██║     ██╔════╝
9
    ██████╔╝ ╚████╔╝ █████╗  ██║   ██║██╔██╗ ██║██║     █████╗  ██████╔╝██║     █████╗
10
    ██╔═══╝   ╚██╔╝  ██╔══╝  ██║   ██║██║╚██╗██║██║     ██╔══╝  ██╔══██╗██║     ██╔══╝
11
    ██║        ██║   ██║     ╚██████╔╝██║ ╚████║╚██████╗███████╗██████╔╝███████╗███████╗
12
    ╚═╝        ╚═╝   ╚═╝      ╚═════╝ ╚═╝  ╚═══╝ ╚═════╝╚══════╝╚═════╝ ╚══════╝╚══════╝
13

14
Provides an interface which let us interact with the Public Suffix List.
15

16
Author:
17
    Nissar Chababy, @funilrys, contactTATAfunilrysTODTODcom
18

19
Special thanks:
20
    https://pyfunceble.github.io/#/special-thanks
21

22
Contributors:
23
    https://pyfunceble.github.io/#/contributors
24

25
Project link:
26
    https://github.com/funilrys/PyFunceble
27

28
Project documentation:
29
    https://docs.pyfunceble.com
30

31
Project homepage:
32
    https://pyfunceble.github.io/
33

34
License:
35
::
36

37

38
    Copyright 2017, 2018, 2019, 2020, 2022, 2023, 2024, 2025 Nissar Chababy
39

40
    Licensed under the Apache License, Version 2.0 (the "License");
41
    you may not use this file except in compliance with the License.
42
    You may obtain a copy of the License at
43

44
        https://www.apache.org/licenses/LICENSE-2.0
45

46
    Unless required by applicable law or agreed to in writing, software
47
    distributed under the License is distributed on an "AS IS" BASIS,
48
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
49
    See the License for the specific language governing permissions and
50
    limitations under the License.
51
"""
52

53
import secrets
9✔
54
from typing import Any, Optional
9✔
55
from warnings import warn
9✔
56

57
import PyFunceble.storage
9✔
58
from PyFunceble.dataset.base import DatasetBase
9✔
59
from PyFunceble.downloader.user_agents import UserAgentsDownloader
9✔
60

61

62
class UserAgentDataset(DatasetBase):
9✔
63
    """
64
    Provides the dataset and infrastructure for the User Agent navigation
65
    """
66

67
    STORAGE_INDEX: str = "USER_AGENTS"
9✔
68
    downloader: Optional[UserAgentsDownloader] = None
9✔
69

70
    preferred_browser: str = "chrome"
9✔
71
    preferred_platform: str = "linux"
9✔
72

73
    def __init__(self) -> None:
74
        self.downloader = UserAgentsDownloader()
75
        self.source_file = self.downloader.destination
76

77
    def __contains__(self, value: Any) -> bool:
9✔
78
        content = self.get_content()
9✔
79

80
        if "@modern" in content:
9✔
81
            return value in self.get_content()["@modern"]
9✔
UNCOV
82
        return value in self.get_content()
×
83

84
    def __getattr__(self, value: Any) -> dict:
9✔
85
        if value in self:
9✔
86
            content = self.get_content()
9✔
87

88
            if "@modern" in content:
9✔
89
                return self.get_content()["@modern"][value]
9✔
UNCOV
90
            return self.get_content()[value]
×
91

92
        return dict()  # pylint: disable=use-dict-literal
9✔
93

94
    def __getitem__(self, value: Any) -> dict:
9✔
95
        return self.__getattr__(value)
9✔
96

97
    def set_preferred(
9✔
98
        self, browser_short_name: str, platform: str
99
    ) -> "UserAgentDataset":
100
        """
101
        Sets the preferred browser to work with.
102

103
        :param browser_short_name:
104
            The name of the browser to select.
105
        :pram platform:
106
            The name of the platform to select.
107

108
        :raise TypeError:
109
            When the given :code:`name` is not a :py:class:`str`.
110
        :raise ValueError:
111
            When the given :code:`value` is not supported.
112
        """
113

114
        if not self.is_supported(browser_short_name, platform):
9✔
115
            raise ValueError(
9✔
116
                f"The given name ({browser_short_name!r}) or platform "
117
                f"({platform!r}) is not supported."
118
            )
119

120
        self.preferred_browser = browser_short_name.lower()
9✔
121
        self.preferred_platform = platform.lower()
9✔
122

123
        return self
9✔
124

125
    # @deprecated("TODO: Soon we should be able to do this...")
126
    def set_prefered(
9✔
127
        self, browser_short_name: str, platform: str
128
    ) -> "UserAgentDataset":
129
        """
130
        Sets the preferred browser to work with.
131

132
        :param browser_short_name:
133
            The name of the browser to select.
134
        :pram platform:
135
            The name of the platform to select.
136

137
        :raise TypeError:
138
            When the given :code:`name` is not a :py:class:`str`.
139
        :raise ValueError:
140
            When the given :code:`value` is not supported.
141
        """
142

UNCOV
143
        warn(
×
144
            "The set_prefered() method is deprecated and will be removed in future "
145
            " releases. Please consider using the set_preferred() method instead.",
146
            DeprecationWarning,
147
            stacklevel=2,
148
        )
149

UNCOV
150
        return self.set_preferred(browser_short_name, platform)
×
151

152
    def is_supported_browser(self, browser_short_name: str) -> bool:
9✔
153
        """
154
        Checks if the given browser is supported.
155

156
        :raise TypeError:
157
            When :code:`browser_short_name` is not a :py:class:`str`.
158
        """
159

160
        if not isinstance(browser_short_name, str):
9✔
161
            raise TypeError(
9✔
162
                f"<browser_short_name> should be {str}, "
163
                f"{type(browser_short_name)} given."
164
            )
165

166
        return bool(browser_short_name.lower() in self) and bool(
9✔
167
            self[browser_short_name.lower()]
168
        )
169

170
    def is_supported(self, browser_short_name: str, platform: str) -> bool:
9✔
171
        """
172
        Checks if the given browser and platform is supported.
173

174
        :param browser_short_name:
175
            The short name of the browser.
176

177
        :param platform:
178
            The platform name.
179

180
        :raise TypeError:
181
            When :code:`browser_short_name` or :code:`platform` are not :py:class:`str`.
182
        """
183

184
        if not isinstance(browser_short_name, str):
9✔
185
            raise TypeError(
9✔
186
                f"<browser_short_name> should be {str}, "
187
                f"{type(browser_short_name)} given."
188
            )
189

190
        if not isinstance(platform, str):
9✔
191
            raise TypeError(f"<platform> should be {str}, {type(platform)} given.")
9✔
192

193
        return (
9✔
194
            self.is_supported_browser(browser_short_name)
195
            and bool(platform.lower() in self[browser_short_name.lower()])
196
            and bool(self[browser_short_name.lower()][platform.lower()])
197
        )
198

199
    def format_user_agent(
9✔
200
        self, user_agent: str, *, reference: Optional[str] = None
201
    ) -> str:
202
        """
203
        Given a user agent and a reference, it returns the formatted user agent
204
        that we have to use.
205

206
        :param user_agent:
207
            The user agent to format.
208
        :param reference:
209
            The reference to append to the user agent.
210

211
        :return:
212
            The formatted user agent.
213
        """
214

215
        user_agent = user_agent.strip()
9✔
216

217
        if reference:
9✔
218
            if user_agent.endswith(";"):
9✔
UNCOV
219
                return f"{user_agent} +{reference}"
×
220
            return f"{user_agent}; +{reference}"
9✔
221
        return user_agent
9✔
222

223
    def get_latest(self) -> str:
9✔
224
        """
225
        Provides the latest user agent for the given platform.
226

227
        Side Effect:
228
            It tries to get the platform and browser from the configuration
229
            (if exists).
230
        """
231

232
        reference = None
9✔
233

234
        if PyFunceble.storage.CONFIGURATION:
9✔
235
            reference = PyFunceble.storage.CONFIGURATION.user_agent.reference
9✔
236

237
            if (
9✔
238
                PyFunceble.storage.CONFIGURATION.user_agent
239
                and PyFunceble.storage.CONFIGURATION.user_agent.custom
240
            ):
241
                return self.format_user_agent(
9✔
242
                    PyFunceble.storage.CONFIGURATION.user_agent.custom,
243
                    reference=reference,
244
                )
245

246
            self.set_preferred(
9✔
247
                PyFunceble.storage.CONFIGURATION.user_agent.browser,
248
                PyFunceble.storage.CONFIGURATION.user_agent.platform,
249
            )
250

251
        result = self[self.preferred_browser][self.preferred_platform]
9✔
252

253
        if isinstance(result, (list, tuple)):
9✔
254
            return self.format_user_agent(
9✔
255
                secrets.choice(result),
256
                reference=reference,
257
            )
258

UNCOV
259
        return self.format_user_agent(result, reference=reference)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc