tagreader 5.1.0__tar.gz → 5.1.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tagreader
3
- Version: 5.1.0
3
+ Version: 5.1.2
4
4
  Summary: Tagreader is a Python package for reading trend data from the OSIsoft PI and Aspen Infoplus.21 IMS systems.
5
5
  Home-page: https://github.com/equinor/tagreader-python
6
6
  License: MIT
@@ -21,18 +21,16 @@ Classifier: Programming Language :: Python :: 3.8
21
21
  Classifier: Programming Language :: Python :: 3.9
22
22
  Classifier: Programming Language :: Python :: 3.10
23
23
  Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
24
25
  Classifier: Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator
25
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
27
  Classifier: Topic :: Utilities
27
28
  Provides-Extra: notebooks
28
29
  Requires-Dist: certifi (>=2023.7.22,<2024.0.0)
29
- Requires-Dist: cryptography (>=41.0.3,<42.0.0)
30
30
  Requires-Dist: diskcache (>=5.6.1,<6.0.0)
31
31
  Requires-Dist: matplotlib (>=3,<4) ; extra == "notebooks"
32
32
  Requires-Dist: notebook (>=7.0.3,<8.0.0) ; extra == "notebooks"
33
33
  Requires-Dist: pandas (>=1)
34
- Requires-Dist: pillow (==10.0.1)
35
- Requires-Dist: pyodbc (>=4,<5)
36
34
  Requires-Dist: requests (>=2,<3)
37
35
  Requires-Dist: requests-kerberos (>=0,<1)
38
36
  Project-URL: Repository, https://github.com/equinor/tagreader-python
@@ -78,6 +76,8 @@ print(c.search("tag*"))
78
76
  df = c.read_tags(["tag1", "tag2"], "18.06.2020 08:00:00", "18.06.2020 09:00:00", 60)
79
77
  ```
80
78
 
79
+ Note, you can add a timeout argument to the search method in order to avoid long-running search queries.
80
+
81
81
  ### Jupyter Notebook Quickstart
82
82
  Jupyter Notebook examples can be found in /examples. In order to run these examples, you need to install the
83
83
  optional dependencies.
@@ -90,10 +90,9 @@ The quickstart Jupyter Notebook can be found [here](https://github.com/equinor/t
90
90
 
91
91
  For more details, see the [Tagreader Docs](https://equinor.github.io/tagreader-python/).
92
92
 
93
- ## Contribute
94
- As Tagreader is an open source project, all contributions are welcome. This includes code, bug reports, issues,
95
- feature requests, and documentation. The preferred way of submitting a contribution is to either create an issue on
96
- GitHub or to fork the project and make a pull request.
93
+ ## Documentation
94
+ The full documentation can be found in [Tagreader Docs](https://equinor.github.io/tagreader-python/)
97
95
 
98
- For starting contributing, see the [contribute section](https://equinor.github.io/tagreader-python/docs/contribute/overview)
96
+ ## Contribute
97
+ To starting contributing, please see [Tagreader Docs - Contribute](https://equinor.github.io/tagreader-python/docs/contribute/overview)
99
98
 
@@ -38,6 +38,8 @@ print(c.search("tag*"))
38
38
  df = c.read_tags(["tag1", "tag2"], "18.06.2020 08:00:00", "18.06.2020 09:00:00", 60)
39
39
  ```
40
40
 
41
+ Note, you can add a timeout argument to the search method in order to avoid long-running search queries.
42
+
41
43
  ### Jupyter Notebook Quickstart
42
44
  Jupyter Notebook examples can be found in /examples. In order to run these examples, you need to install the
43
45
  optional dependencies.
@@ -50,9 +52,8 @@ The quickstart Jupyter Notebook can be found [here](https://github.com/equinor/t
50
52
 
51
53
  For more details, see the [Tagreader Docs](https://equinor.github.io/tagreader-python/).
52
54
 
53
- ## Contribute
54
- As Tagreader is an open source project, all contributions are welcome. This includes code, bug reports, issues,
55
- feature requests, and documentation. The preferred way of submitting a contribution is to either create an issue on
56
- GitHub or to fork the project and make a pull request.
55
+ ## Documentation
56
+ The full documentation can be found in [Tagreader Docs](https://equinor.github.io/tagreader-python/)
57
57
 
58
- For starting contributing, see the [contribute section](https://equinor.github.io/tagreader-python/docs/contribute/overview)
58
+ ## Contribute
59
+ To starting contributing, please see [Tagreader Docs - Contribute](https://equinor.github.io/tagreader-python/docs/contribute/overview)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "tagreader"
3
- version = "5.1.0"
3
+ version = "5.1.2"
4
4
  description = "Tagreader is a Python package for reading trend data from the OSIsoft PI and Aspen Infoplus.21 IMS systems."
5
5
  authors = ["Einar S. Idsø <eiids@equinor.com>", "Morten Dæhli Aslesen <masl@equinor.com"]
6
6
  license = "MIT"
@@ -30,15 +30,12 @@ classifiers=[
30
30
  [tool.poetry.dependencies]
31
31
  python = "^3.8"
32
32
  pandas = ">=1"
33
- pyodbc = "^4"
34
- cryptography = "^41.0.3"
35
33
  certifi = "^2023.7.22"
36
34
  requests = "^2"
37
35
  requests-kerberos = "^0"
38
36
  notebook = { version = "^7.0.3", optional = true }
39
37
  matplotlib = { version = "^3", optional = true }
40
38
  diskcache = "^5.6.1"
41
- pillow = "10.0.1" # Fix dependency to avoid vulnerability
42
39
 
43
40
 
44
41
  [tool.poetry.group.dev.dependencies]
@@ -5,6 +5,7 @@ from operator import itemgetter
5
5
  from typing import Any, Dict, List, Optional, Tuple, Union
6
6
  from urllib.error import HTTPError
7
7
 
8
+ import numpy as np
8
9
  import pandas as pd
9
10
  import pytz
10
11
 
@@ -199,6 +200,7 @@ def get_handler(
199
200
  options: Dict[str, Union[int, float, str]],
200
201
  verifySSL: Optional[bool],
201
202
  auth: Optional[Any],
203
+ cache: Optional[Union[SmartCache, BucketCache]] = None,
202
204
  ):
203
205
  if imstype is None:
204
206
  try:
@@ -224,6 +226,7 @@ def get_handler(
224
226
  options=options,
225
227
  verify_ssl=verifySSL,
226
228
  auth=auth,
229
+ cache=cache,
227
230
  )
228
231
 
229
232
  if imstype == IMSType.ASPENONE:
@@ -278,6 +281,7 @@ class IMSClient:
278
281
  f"timezone argument 'tz' needs to be either a valid timezone string or a tzinfo-object. Given type was {type(tz)}"
279
282
  )
280
283
 
284
+ self.cache = cache
281
285
  self.handler = get_handler(
282
286
  imstype=imstype,
283
287
  datasource=datasource,
@@ -285,22 +289,28 @@ class IMSClient:
285
289
  options=handler_options,
286
290
  verifySSL=verifySSL,
287
291
  auth=auth,
292
+ cache=self.cache,
288
293
  )
289
- self.cache = cache
290
294
 
291
295
  def connect(self) -> None:
292
296
  self.handler.connect()
293
297
 
294
298
  def search_tag(
295
- self, tag: Optional[str] = None, desc: Optional[str] = None
299
+ self,
300
+ tag: Optional[str] = None,
301
+ desc: Optional[str] = None,
302
+ timeout: Optional[int] = None,
296
303
  ) -> List[Tuple[str, str]]:
297
304
  logger.warning("This function is deprecated. Please call 'search()' instead")
298
- return self.search(tag=tag, desc=desc)
305
+ return self.search(tag=tag, desc=desc, timeout=timeout)
299
306
 
300
307
  def search(
301
- self, tag: Optional[str] = None, desc: Optional[str] = None
308
+ self,
309
+ tag: Optional[str] = None,
310
+ desc: Optional[str] = None,
311
+ timeout: Optional[int] = None,
302
312
  ) -> List[Tuple[str, str]]:
303
- return self.handler.search(tag=tag, desc=desc)
313
+ return self.handler.search(tag=tag, desc=desc, timeout=timeout)
304
314
 
305
315
  def _get_metadata(self, tag: str):
306
316
  return self.handler._get_tag_metadata(
@@ -487,7 +497,7 @@ class IMSClient:
487
497
  tags: Union[str, List[str]],
488
498
  start_time: Optional[Union[datetime, pd.Timestamp, str]] = None,
489
499
  end_time: Optional[Union[datetime, pd.Timestamp, str]] = None,
490
- ts: Union[timedelta, pd.Timedelta, int] = timedelta(seconds=60),
500
+ ts: Optional[Union[timedelta, pd.Timedelta, int]] = timedelta(seconds=60),
491
501
  read_type: ReaderType = ReaderType.INT,
492
502
  get_status: bool = False,
493
503
  ) -> pd.DataFrame:
@@ -540,8 +550,26 @@ class IMSClient:
540
550
 
541
551
  if isinstance(ts, pd.Timedelta):
542
552
  ts = ts.to_pytimedelta()
543
- elif isinstance(ts, (int, float)):
553
+ elif isinstance(
554
+ ts,
555
+ (
556
+ int,
557
+ float,
558
+ np.int32,
559
+ np.int64,
560
+ np.float32,
561
+ np.float64,
562
+ np.number,
563
+ np.integer,
564
+ ),
565
+ ):
544
566
  ts = timedelta(seconds=int(ts))
567
+ elif not ts and read_type not in [ReaderType.SNAPSHOT, ReaderType.RAW]:
568
+ raise ValueError(
569
+ "ts needs to be a timedelta or an integer (number of seconds)"
570
+ " unless you are reading raw or snapshot data."
571
+ f" Given type: {type(ts)}"
572
+ )
545
573
  elif not isinstance(ts, timedelta):
546
574
  raise ValueError(
547
575
  "ts needs to be either a None, timedelta or and integer (number of seconds)."
@@ -130,7 +130,7 @@ class ReaderType(enum.IntEnum):
130
130
 
131
131
 
132
132
  def add_statoil_root_certificate() -> bool:
133
- return add_equinor_root_certificate(True) and add_equinor_root_certificate(False)
133
+ return add_equinor_root_certificate(True) or add_equinor_root_certificate(False)
134
134
 
135
135
 
136
136
  def add_equinor_root_certificate(get_equinor: bool = True) -> bool:
@@ -14,7 +14,7 @@ import requests
14
14
  import urllib3
15
15
  from requests_kerberos import OPTIONAL, HTTPKerberosAuth
16
16
 
17
- from tagreader.cache import BaseCache
17
+ from tagreader.cache import BaseCache, BucketCache, SmartCache
18
18
  from tagreader.logger import logger
19
19
  from tagreader.utils import ReaderType, is_mac, is_windows, urljoin
20
20
 
@@ -115,8 +115,20 @@ class BaseHandlerWeb(ABC):
115
115
  urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
116
116
  self.session.verify = verify_ssl if verify_ssl is not None else get_verify_ssl()
117
117
 
118
- def fetch(self, url, params: Optional[Union[str, Dict[str, str]]] = None) -> Dict:
119
- res = self.session.get(url, params=params)
118
+ def fetch(
119
+ self,
120
+ url,
121
+ params: Optional[Union[str, Dict[str, str]]] = None,
122
+ timeout: Optional[int] = None,
123
+ ) -> Dict:
124
+ res = self.session.get(
125
+ url,
126
+ params=params,
127
+ timeout=(
128
+ None,
129
+ timeout,
130
+ ),
131
+ ) # Noqa. Read timeout, No connect timeout.
120
132
  res.raise_for_status()
121
133
 
122
134
  if len(res.text) == 0:
@@ -366,7 +378,9 @@ class AspenHandlerWeb(BaseHandlerWeb):
366
378
  if v:
367
379
  return k
368
380
 
369
- def search(self, tag: Optional[str], desc: Optional[str]) -> List[Tuple[str, str]]:
381
+ def search(
382
+ self, tag: Optional[str], desc: Optional[str], timeout: Optional[int] = None
383
+ ) -> List[Tuple[str, str]]:
370
384
  if tag is None:
371
385
  raise ValueError("Tag is a required argument")
372
386
 
@@ -386,7 +400,7 @@ class AspenHandlerWeb(BaseHandlerWeb):
386
400
  )
387
401
  url = urljoin(self.base_url, "Browse?")
388
402
  url += encoded_params
389
- data = self.fetch(url)
403
+ data = self.fetch(url, timeout=timeout)
390
404
 
391
405
  if "tags" not in data["data"]:
392
406
  return []
@@ -602,6 +616,7 @@ class PIHandlerWeb(BaseHandlerWeb):
602
616
  auth: Optional[Any],
603
617
  verify_ssl: bool,
604
618
  options: Dict[str, Union[int, float, str]],
619
+ cache: Optional[Union[SmartCache, BucketCache]],
605
620
  ):
606
621
  self._max_rows = options.get("max_rows", 10000)
607
622
  if url is None:
@@ -615,7 +630,7 @@ class PIHandlerWeb(BaseHandlerWeb):
615
630
  verify_ssl=verify_ssl,
616
631
  )
617
632
  self._max_rows = options.get("max_rows", 10000)
618
- self.web_id_cache = BaseCache(directory=Path(".") / ".cache" / datasource)
633
+ self.web_id_cache = cache
619
634
 
620
635
  @staticmethod
621
636
  def _time_to_UTC_string(time: datetime) -> str:
@@ -772,7 +787,10 @@ class PIHandlerWeb(BaseHandlerWeb):
772
787
  return False
773
788
 
774
789
  def search(
775
- self, tag: Optional[str] = None, desc: Optional[str] = None
790
+ self,
791
+ tag: Optional[str] = None,
792
+ desc: Optional[str] = None,
793
+ timeout: Optional[int] = None,
776
794
  ) -> List[Tuple]:
777
795
  params = self.generate_search_query(
778
796
  tag=tag, desc=desc, datasource=self.datasource
@@ -781,7 +799,7 @@ class PIHandlerWeb(BaseHandlerWeb):
781
799
  done = False
782
800
  ret = []
783
801
  while not done:
784
- data = self.fetch(url, params=params)
802
+ data = self.fetch(url, params=params, timeout=timeout)
785
803
 
786
804
  for item in data["Items"]:
787
805
  description = item["Description"] if "Description" in item else ""
@@ -823,33 +841,37 @@ class PIHandlerWeb(BaseHandlerWeb):
823
841
  :return: WebId
824
842
  :rtype: str
825
843
  """
826
- if tag not in self.web_id_cache:
827
- params = self.generate_search_query(
828
- tag=tag, datasource=self.datasource, desc=None
829
- )
830
- params["fields"] = "name;webid"
831
- url = urljoin(self.base_url, "search", "query")
832
- data = self.fetch(url, params=params)
833
-
834
- if len(data["Errors"]) > 0:
835
- msg = f"Received error from server when searching for WebId for {tag}: {data['Errors']}"
836
- raise ValueError(msg)
837
-
838
- if len(data["Items"]) > 1:
839
- # Compare elements and if same, return the first
840
- first = data["Items"][0]
841
- for item in data["Items"][1:]:
842
- if item != first:
843
- raise AssertionError(
844
- f"Received {len(data['Items'])} results when trying to find unique WebId for {tag}."
845
- )
846
- elif len(data["Items"]) == 0:
847
- logger.warning(f"Tag {tag} not found")
848
- return None
849
-
850
- web_id = data["Items"][0]["WebId"]
844
+ if self.web_id_cache and tag in self.web_id_cache:
845
+ return self.web_id_cache[tag]
846
+
847
+ params = self.generate_search_query(
848
+ tag=tag, datasource=self.datasource, desc=None
849
+ )
850
+ params["fields"] = "name;webid"
851
+ url = urljoin(self.base_url, "search", "query")
852
+ data = self.fetch(url, params=params)
853
+
854
+ if len(data["Errors"]) > 0:
855
+ msg = f"Received error from server when searching for WebId for {tag}: {data['Errors']}"
856
+ raise ValueError(msg)
857
+
858
+ if len(data["Items"]) > 1:
859
+ # Compare elements and if same, return the first
860
+ first = data["Items"][0]
861
+ for item in data["Items"][1:]:
862
+ if item != first:
863
+ raise AssertionError(
864
+ f"Received {len(data['Items'])} results when trying to find unique WebId for {tag}."
865
+ )
866
+ elif len(data["Items"]) == 0:
867
+ logger.warning(f"Tag {tag} not found")
868
+ return None
869
+
870
+ web_id = data["Items"][0]["WebId"]
871
+
872
+ if self.web_id_cache:
851
873
  self.web_id_cache[tag] = web_id
852
- return self.web_id_cache[tag]
874
+ return web_id
853
875
 
854
876
  @staticmethod
855
877
  def _is_summary(read_type: ReaderType) -> bool:
File without changes
File without changes
File without changes