pyalex 0.12__py3-none-any.whl → 0.14__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.
pyalex/__init__.py CHANGED
@@ -9,6 +9,10 @@ from pyalex.api import Author
9
9
  from pyalex.api import Authors
10
10
  from pyalex.api import Concept
11
11
  from pyalex.api import Concepts
12
+ from pyalex.api import Domain
13
+ from pyalex.api import Domains
14
+ from pyalex.api import Field
15
+ from pyalex.api import Fields
12
16
  from pyalex.api import Funder
13
17
  from pyalex.api import Funders
14
18
  from pyalex.api import Institution
@@ -19,10 +23,15 @@ from pyalex.api import Publisher
19
23
  from pyalex.api import Publishers
20
24
  from pyalex.api import Source
21
25
  from pyalex.api import Sources
26
+ from pyalex.api import Subfield
27
+ from pyalex.api import Subfields
28
+ from pyalex.api import Topic
29
+ from pyalex.api import Topics
22
30
  from pyalex.api import Venue
23
31
  from pyalex.api import Venues
24
32
  from pyalex.api import Work
25
33
  from pyalex.api import Works
34
+ from pyalex.api import autocomplete
26
35
  from pyalex.api import config
27
36
  from pyalex.api import invert_abstract
28
37
 
@@ -43,8 +52,17 @@ __all__ = [
43
52
  "Institution",
44
53
  "Concepts",
45
54
  "Concept",
55
+ "Domains",
56
+ "Domain",
57
+ "Fields",
58
+ "Field",
59
+ "Subfields",
60
+ "Subfield",
61
+ "Topics",
62
+ "Topic",
46
63
  "People",
47
64
  "Journals",
65
+ "autocomplete",
48
66
  "config",
49
67
  "invert_abstract",
50
68
  ]
pyalex/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.12'
16
- __version_tuple__ = version_tuple = (0, 12)
15
+ __version__ = version = '0.14'
16
+ __version_tuple__ = version_tuple = (0, 14)
pyalex/api.py CHANGED
@@ -3,6 +3,7 @@ import warnings
3
3
  from urllib.parse import quote_plus
4
4
 
5
5
  import requests
6
+ from requests.auth import AuthBase
6
7
  from urllib3.util import Retry
7
8
 
8
9
  try:
@@ -22,6 +23,7 @@ class AlexConfig(dict):
22
23
  config = AlexConfig(
23
24
  email=None,
24
25
  api_key=None,
26
+ user_agent="pyalex/" + __version__,
25
27
  openalex_url="https://api.openalex.org",
26
28
  max_retries=0,
27
29
  retry_backoff_factor=0.1,
@@ -160,6 +162,32 @@ class Paginator:
160
162
  return results
161
163
 
162
164
 
165
+ class OpenAlexAuth(AuthBase):
166
+ """OpenAlex auth class based on requests auth
167
+
168
+ Includes the email, api_key and user-agent headers.
169
+
170
+ arguments:
171
+ config: an AlexConfig object
172
+
173
+ """
174
+
175
+ def __init__(self, config):
176
+ self.config = config
177
+
178
+ def __call__(self, r):
179
+ if self.config.api_key:
180
+ r.headers["Authorization"] = f"Bearer {self.config.api_key}"
181
+
182
+ if self.config.email:
183
+ r.headers["From"] = self.config.email
184
+
185
+ if self.config.user_agent:
186
+ r.headers["User-Agent"] = self.config.user_agent
187
+
188
+ return r
189
+
190
+
163
191
  class BaseOpenAlex:
164
192
  """Base class for OpenAlex objects."""
165
193
 
@@ -170,7 +198,11 @@ class BaseOpenAlex:
170
198
  return self.filter(openalex_id="|".join(record_list)).get()
171
199
 
172
200
  def _full_collection_name(self):
173
- return config.openalex_url + "/" + self.__class__.__name__.lower()
201
+ if self.params is not None and "q" in self.params.keys():
202
+ base_url = config.openalex_url + "/autocomplete/"
203
+ return base_url + self.__class__.__name__.lower()
204
+ else:
205
+ return config.openalex_url + "/" + self.__class__.__name__.lower()
174
206
 
175
207
  def __getattr__(self, key):
176
208
  if key == "groupby":
@@ -222,13 +254,7 @@ class BaseOpenAlex:
222
254
  return m["count"]
223
255
 
224
256
  def _get_from_url(self, url, return_meta=False):
225
- params = {"api_key": config.api_key} if config.api_key else {}
226
-
227
- res = _get_requests_session().get(
228
- url,
229
- headers={"User-Agent": "pyalex/" + __version__, "email": config.email},
230
- params=params,
231
- )
257
+ res = _get_requests_session().get(url, auth=OpenAlexAuth(config))
232
258
 
233
259
  # handle query errors
234
260
  if res.status_code == 403:
@@ -264,7 +290,6 @@ class BaseOpenAlex:
264
290
  self._add_params("per-page", per_page)
265
291
  self._add_params("page", page)
266
292
  self._add_params("cursor", cursor)
267
-
268
293
  return self._get_from_url(self.url, return_meta=return_meta)
269
294
 
270
295
  def paginate(self, method="cursor", page=1, per_page=None, cursor="*", n_max=10000):
@@ -321,6 +346,11 @@ class BaseOpenAlex:
321
346
  self._add_params("select", s)
322
347
  return self
323
348
 
349
+ def autocomplete(self, s, **kwargs):
350
+ """autocomplete the string s, for a specific type of entity"""
351
+ self._add_params("q", s)
352
+ return self.get(**kwargs)
353
+
324
354
 
325
355
  # The API
326
356
 
@@ -334,11 +364,9 @@ class Work(OpenAlexEntity):
334
364
 
335
365
  def ngrams(self, return_meta=False):
336
366
  openalex_id = self["id"].split("/")[-1]
367
+ n_gram_url = f"{config.openalex_url}/works/{openalex_id}/ngrams"
337
368
 
338
- res = _get_requests_session().get(
339
- f"{config.openalex_url}/works/{openalex_id}/ngrams",
340
- headers={"User-Agent": "pyalex/" + __version__, "email": config.email},
341
- )
369
+ res = _get_requests_session().get(n_gram_url, auth=OpenAlexAuth(config))
342
370
  res.raise_for_status()
343
371
  results = res.json()
344
372
 
@@ -377,12 +405,36 @@ class Institutions(BaseOpenAlex):
377
405
  resource_class = Institution
378
406
 
379
407
 
380
- class Concept(OpenAlexEntity):
408
+ class Domain(OpenAlexEntity):
381
409
  pass
382
410
 
383
411
 
384
- class Concepts(BaseOpenAlex):
385
- resource_class = Concept
412
+ class Domains(BaseOpenAlex):
413
+ resource_class = Domain
414
+
415
+
416
+ class Field(OpenAlexEntity):
417
+ pass
418
+
419
+
420
+ class Fields(BaseOpenAlex):
421
+ resource_class = Field
422
+
423
+
424
+ class Subfield(OpenAlexEntity):
425
+ pass
426
+
427
+
428
+ class Subfields(BaseOpenAlex):
429
+ resource_class = Subfield
430
+
431
+
432
+ class Topic(OpenAlexEntity):
433
+ pass
434
+
435
+
436
+ class Topics(BaseOpenAlex):
437
+ resource_class = Topic
386
438
 
387
439
 
388
440
  class Publisher(OpenAlexEntity):
@@ -401,6 +453,21 @@ class Funders(BaseOpenAlex):
401
453
  resource_class = Funder
402
454
 
403
455
 
456
+ class Autocomplete(OpenAlexEntity):
457
+ pass
458
+
459
+
460
+ class autocompletes(BaseOpenAlex):
461
+ """Class to autocomplete without being based on the type of entity"""
462
+
463
+ resource_class = Autocomplete
464
+
465
+ def __getitem__(self, key):
466
+ return self._get_from_url(
467
+ config.openalex_url + "/autocomplete" + "?q=" + key, return_meta=False
468
+ )
469
+
470
+
404
471
  def Venue(*args, **kwargs): # deprecated
405
472
  # warn about deprecation
406
473
  warnings.warn(
@@ -423,6 +490,30 @@ def Venues(*args, **kwargs): # deprecated
423
490
  return Sources(*args, **kwargs)
424
491
 
425
492
 
493
+ class Concept(OpenAlexEntity):
494
+ # warn about deprecation
495
+ warnings.warn(
496
+ "Concept is deprecated by OpenAlex and replaced by topics.",
497
+ DeprecationWarning,
498
+ stacklevel=2,
499
+ )
500
+
501
+
502
+ class Concepts(BaseOpenAlex):
503
+ # warn about deprecation
504
+ warnings.warn(
505
+ "Concepts is deprecated by OpenAlex and replaced by topics.",
506
+ DeprecationWarning,
507
+ stacklevel=2,
508
+ )
509
+ resource_class = Concept
510
+
511
+
512
+ def autocomplete(s):
513
+ """autocomplete with any type of entity"""
514
+ return autocompletes()[s]
515
+
516
+
426
517
  # aliases
427
518
  People = Authors
428
519
  Journals = Sources
@@ -1,18 +1,17 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyalex
3
- Version: 0.12
3
+ Version: 0.14
4
4
  Summary: Python interface to the OpenAlex database
5
5
  Author-email: Jonathan de Bruin <jonathandebruinos@gmail.com>
6
6
  License: MIT
7
7
  Classifier: Development Status :: 5 - Production/Stable
8
8
  Classifier: License :: OSI Approved :: MIT License
9
- Classifier: Programming Language :: Python :: 3.6
10
- Classifier: Programming Language :: Python :: 3.7
11
9
  Classifier: Programming Language :: Python :: 3.8
12
10
  Classifier: Programming Language :: Python :: 3.9
13
11
  Classifier: Programming Language :: Python :: 3.10
14
12
  Classifier: Programming Language :: Python :: 3.11
15
- Requires-Python: >=3.6
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Python: >=3.8
16
15
  Description-Content-Type: text/markdown
17
16
  License-File: LICENSE
18
17
  Requires-Dist: requests
@@ -21,6 +20,7 @@ Provides-Extra: lint
21
20
  Requires-Dist: ruff ; extra == 'lint'
22
21
  Provides-Extra: test
23
22
  Requires-Dist: pytest ; extra == 'test'
23
+ Requires-Dist: pytest-xdist ; extra == 'test'
24
24
 
25
25
  <p align="center">
26
26
  <img alt="PyAlex - a Python wrapper for OpenAlex" src="https://github.com/J535D165/pyalex/raw/main/pyalex_repocard.svg">
@@ -30,6 +30,7 @@ Requires-Dist: pytest ; extra == 'test'
30
30
 
31
31
  ![PyPI](https://img.shields.io/pypi/v/pyalex) [![DOI](https://zenodo.org/badge/557541347.svg)](https://zenodo.org/badge/latestdoi/557541347)
32
32
 
33
+
33
34
  PyAlex is a Python library for [OpenAlex](https://openalex.org/). OpenAlex is
34
35
  an index of hundreds of millions of interconnected scholarly papers, authors,
35
36
  institutions, and more. OpenAlex offers a robust, open, and free [REST API](https://docs.openalex.org/) to extract, aggregate, or search scholarly data.
@@ -46,7 +47,7 @@ The following features of OpenAlex are currently supported by PyAlex:
46
47
  - [x] Select fields
47
48
  - [x] Sample
48
49
  - [x] Pagination
49
- - [ ] [Autocomplete endpoint](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/autocomplete-entities)
50
+ - [x] Autocomplete endpoint
50
51
  - [x] N-grams
51
52
  - [x] Authentication
52
53
 
@@ -60,7 +61,7 @@ We aim to cover the entire API, and we are looking for help. We are welcoming Pu
60
61
 
61
62
  ## Installation
62
63
 
63
- PyAlex requires Python 3.6 or later.
64
+ PyAlex requires Python 3.8 or later.
64
65
 
65
66
  ```sh
66
67
  pip install pyalex
@@ -68,10 +69,10 @@ pip install pyalex
68
69
 
69
70
  ## Getting started
70
71
 
71
- PyAlex offers support for all [Entity Objects](https://docs.openalex.org/api-entities/entities-overview): [Works](https://docs.openalex.org/api-entities/works), [Authors](https://docs.openalex.org/api-entities/authors), [Sources](https://docs.openalex.org/api-entities/sourcese), [Institutions](https://docs.openalex.org/api-entities/institutions), [Concepts](https://docs.openalex.org/api-entities/concepts), [Publishers](https://docs.openalex.org/api-entities/publishers), and [Funders](https://docs.openalex.org/api-entities/funders).
72
+ PyAlex offers support for all [Entity Objects](https://docs.openalex.org/api-entities/entities-overview): [Works](https://docs.openalex.org/api-entities/works), [Authors](https://docs.openalex.org/api-entities/authors), [Sources](https://docs.openalex.org/api-entities/sourcese), [Institutions](https://docs.openalex.org/api-entities/institutions), [Topics](https://docs.openalex.org/api-entities/topics), [Publishers](https://docs.openalex.org/api-entities/publishers), and [Funders](https://docs.openalex.org/api-entities/funders).
72
73
 
73
74
  ```python
74
- from pyalex import Works, Authors, Sources, Institutions, Concepts, Publishers, Funders
75
+ from pyalex import Works, Authors, Sources, Institutions, Topics, Publishers, Funders
75
76
  ```
76
77
 
77
78
  ### The polite pool
@@ -86,9 +87,21 @@ import pyalex
86
87
  pyalex.config.email = "mail@example.com"
87
88
  ```
88
89
 
90
+ ### Max retries
91
+
92
+ By default, PyAlex will raise an error at the first failure when querying the OpenAlex API. You can set `max_retries` to a number higher than 0 to allow PyAlex to retry when an error occurs. `retry_backoff_factor` is related to the delay between two retry, and `retry_http_codes` are the HTTP error codes that should trigger a retry.
93
+
94
+ ```python
95
+ from pyalex import config
96
+
97
+ config.max_retries = 0
98
+ config.retry_backoff_factor = 0.1
99
+ config.retry_http_codes = [429, 500, 503]
100
+ ```
101
+
89
102
  ### Get single entity
90
103
 
91
- Get a single Work, Author, Source, Institution, Concept, Publisher or Funder from OpenAlex by the
104
+ Get a single Work, Author, Source, Institution, Concept, Topic, Publisher or Funder from OpenAlex by the
92
105
  OpenAlex ID, or by DOI or ROR.
93
106
 
94
107
  ```python
@@ -110,7 +123,7 @@ Works()["W2741809807"]["open_access"]
110
123
  {'is_oa': True, 'oa_status': 'gold', 'oa_url': 'https://doi.org/10.7717/peerj.4375'}
111
124
  ```
112
125
 
113
- The previous works also for Authors, Venues, Institutions and Concepts
126
+ The previous works also for Authors, Venues, Institutions, Concepts and Topics
114
127
 
115
128
  ```python
116
129
  Authors()["A2887243803"]
@@ -119,7 +132,7 @@ Authors()["https://orcid.org/0000-0002-4297-0502"] # same
119
132
 
120
133
  #### Get random
121
134
 
122
- Get a [random Work, Author, Source, Institution, Concept, Publisher or Funder](https://docs.openalex.org/how-to-use-the-api/get-single-entities/random-result).
135
+ Get a [random Work, Author, Source, Institution, Concept, Topic, Publisher or Funder](https://docs.openalex.org/how-to-use-the-api/get-single-entities/random-result).
123
136
 
124
137
  ```python
125
138
  Works().random()
@@ -127,6 +140,7 @@ Authors().random()
127
140
  Sources().random()
128
141
  Institutions().random()
129
142
  Concepts().random()
143
+ Topics().random()
130
144
  Publishers().random()
131
145
  Funders().random()
132
146
  ```
@@ -169,7 +183,7 @@ Works().count()
169
183
  For lists of entities, you can return the result as well as the metadata. By default, only the results are returned.
170
184
 
171
185
  ```python
172
- results, meta = Concepts().get(return_meta=True)
186
+ results, meta = Topics().get(return_meta=True)
173
187
  ```
174
188
 
175
189
  ```python
@@ -328,6 +342,32 @@ for page in pager:
328
342
  ```
329
343
 
330
344
 
345
+ ### Autocomplete
346
+
347
+ OpenAlex reference: [Autocomplete entities](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/autocomplete-entities).
348
+
349
+ Autocomplete a string:
350
+ ```python
351
+ from pyalex import autocomplete
352
+
353
+ autocomplete("stockholm resilience centre")
354
+ ```
355
+
356
+ Autocomplete a string to get a specific type of entities:
357
+ ```python
358
+ from pyalex import Institutions
359
+
360
+ Institutions().autocomplete("stockholm resilience centre")
361
+ ```
362
+
363
+ You can also use the filters to autocomplete:
364
+ ```python
365
+ from pyalex import Works
366
+
367
+ r = Works().filter(publication_year=2023).autocomplete("planetary boundaries")
368
+ ```
369
+
370
+
331
371
  ### Get N-grams
332
372
 
333
373
  OpenAlex reference: [Get N-grams](https://docs.openalex.org/api-entities/works/get-n-grams).
@@ -412,6 +452,8 @@ R users can use the excellent [OpenAlexR](https://github.com/ropensci/openalexR)
412
452
 
413
453
  ## Contact
414
454
 
455
+ > This library is a community contribution. The authors of this Python library aren't affiliated with OpenAlex.
456
+
415
457
  Feel free to reach out with questions, remarks, and suggestions. The
416
458
  [issue tracker](/issues) is a good starting point. You can also email me at
417
459
  [jonathandebruinos@gmail.com](mailto:jonathandebruinos@gmail.com).
@@ -0,0 +1,8 @@
1
+ pyalex/__init__.py,sha256=EL6oh0vQdSNyvsrxFFgmDn-P_Dgev1IYpP-WiLBxjS8,1553
2
+ pyalex/_version.py,sha256=lmFPHiet63Bn9h104yd9q0T6i7C2c0WCbizqrpAtSuk,408
3
+ pyalex/api.py,sha256=whUv8TbnW968wlR8U4O-lWsaFHRgW0Ham0cznbR3Zlc,13212
4
+ pyalex-0.14.dist-info/LICENSE,sha256=Mhf5MImRYP06a1EPVJCpkpTstOOEfGajN3T_Fz4izMg,1074
5
+ pyalex-0.14.dist-info/METADATA,sha256=3bjn78df3VlosFCcseDGs5tfTUIAgCStI_uGjqHT69w,13777
6
+ pyalex-0.14.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
7
+ pyalex-0.14.dist-info/top_level.txt,sha256=D0An8hWy9e0xPhTaT6K-yuJKVeVV3bYGxZ6Y-v2WXSU,7
8
+ pyalex-0.14.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,8 +0,0 @@
1
- pyalex/__init__.py,sha256=UrEW9s9NbULtmmnYUUgfbrDujleV8XQLiMRYGdRnm9M,1137
2
- pyalex/_version.py,sha256=eOKjFr4-OgtTmAgooUIWvbplNekszFVZVHe7TLPOdeU,408
3
- pyalex/api.py,sha256=lmbJaU3GCwi2ZNeshQV0dFXK-j83A08q1yjuVdeEKeM,11193
4
- pyalex-0.12.dist-info/LICENSE,sha256=Mhf5MImRYP06a1EPVJCpkpTstOOEfGajN3T_Fz4izMg,1074
5
- pyalex-0.12.dist-info/METADATA,sha256=JVSzitj9HrRO7us_49vrDWCYIVLe7QtvRNIGGk0tQQU,12632
6
- pyalex-0.12.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
7
- pyalex-0.12.dist-info/top_level.txt,sha256=D0An8hWy9e0xPhTaT6K-yuJKVeVV3bYGxZ6Y-v2WXSU,7
8
- pyalex-0.12.dist-info/RECORD,,
File without changes