tagreader 5.1.3__tar.gz → 5.2.0__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.3
3
+ Version: 5.2.0
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
@@ -29,10 +29,13 @@ Provides-Extra: notebooks
29
29
  Requires-Dist: certifi (>=2023.7.22,<2024.0.0)
30
30
  Requires-Dist: diskcache (>=5.6.1,<6.0.0)
31
31
  Requires-Dist: matplotlib (>=3,<4) ; extra == "notebooks"
32
+ Requires-Dist: msal-bearer (>=0.1.0,<0.2.0)
32
33
  Requires-Dist: notebook (>=7.0.3,<8.0.0) ; extra == "notebooks"
33
34
  Requires-Dist: pandas (>=1)
35
+ Requires-Dist: pycryptodome (>=3.20.0,<4.0.0)
34
36
  Requires-Dist: requests (>=2,<3)
35
37
  Requires-Dist: requests-kerberos (>=0,<1)
38
+ Requires-Dist: requests-ntlm (>=1.2.0,<2.0.0)
36
39
  Project-URL: Repository, https://github.com/equinor/tagreader-python
37
40
  Description-Content-Type: text/markdown
38
41
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "tagreader"
3
- version = "5.1.3"
3
+ version = "5.2.0"
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"
@@ -33,9 +33,12 @@ pandas = ">=1"
33
33
  certifi = "^2023.7.22"
34
34
  requests = "^2"
35
35
  requests-kerberos = "^0"
36
+ msal-bearer = "^0.1.0"
36
37
  notebook = { version = "^7.0.3", optional = true }
37
38
  matplotlib = { version = "^3", optional = true }
38
39
  diskcache = "^5.6.1"
40
+ pycryptodome = "^3.20.0"
41
+ requests-ntlm = "^1.2.0"
39
42
 
40
43
 
41
44
  [tool.poetry.group.dev.dependencies]
@@ -300,7 +300,7 @@ class IMSClient:
300
300
  tag: Optional[str] = None,
301
301
  desc: Optional[str] = None,
302
302
  timeout: Optional[int] = None,
303
- ) -> List[Tuple[str, str]]:
303
+ ) -> Union[List[Tuple[str, str]], List[str]]:
304
304
  logger.warning("This function is deprecated. Please call 'search()' instead")
305
305
  return self.search(tag=tag, desc=desc, timeout=timeout)
306
306
 
@@ -309,8 +309,11 @@ class IMSClient:
309
309
  tag: Optional[str] = None,
310
310
  desc: Optional[str] = None,
311
311
  timeout: Optional[int] = None,
312
- ) -> List[Tuple[str, str]]:
313
- return self.handler.search(tag=tag, desc=desc, timeout=timeout)
312
+ return_desc: bool = True,
313
+ ) -> Union[List[Tuple[str, str]], List[str]]:
314
+ return self.handler.search(
315
+ tag=tag, desc=desc, timeout=timeout, return_desc=return_desc
316
+ )
314
317
 
315
318
  def _get_metadata(self, tag: str):
316
319
  return self.handler._get_tag_metadata(
@@ -171,13 +171,12 @@ def add_equinor_root_certificate(get_equinor: bool = True) -> bool:
171
171
  logger.debug("Scanning CA certificate in Windows cert store", end="")
172
172
  for cert in ssl.enum_certificates("CA"):
173
173
  der = cert[0]
174
+ # deepcode ignore InsecureHash: <Only hashes to compare with known hash>
174
175
  if hashlib.sha1(der).hexdigest().upper() == used_hash:
175
176
  found = True
176
177
  logger.debug("CA certificate found!")
177
178
  break
178
179
  elif is_mac():
179
- import subprocess
180
-
181
180
  macos_ca_certs = subprocess.run(
182
181
  ["security", "find-certificate", "-a", "-c", ca_search, "-Z"],
183
182
  stdout=subprocess.PIPE,
@@ -186,6 +185,7 @@ def add_equinor_root_certificate(get_equinor: bool = True) -> bool:
186
185
  if used_hash in str(macos_ca_certs).upper():
187
186
  c = get_macos_equinor_certificates()
188
187
  for cert in c:
188
+ # deepcode ignore InsecureHash: <Only hashes to compare with known hash>
189
189
  if hashlib.sha1(cert).hexdigest().upper() == used_hash:
190
190
  der = cert
191
191
  found = True
@@ -260,6 +260,7 @@ def is_equinor() -> bool:
260
260
 
261
261
  host = socket.gethostname()
262
262
 
263
+ # deepcode ignore IdenticalBranches: Not an error. First test is just more precise.
263
264
  if host + ".client.statoil.net" in str(s):
264
265
  return True
265
266
  elif "client.statoil.net" in host and host in str(s):
@@ -1,10 +1,11 @@
1
+ import hashlib
1
2
  import json
2
3
  import re
3
4
  import urllib.parse
4
5
  from abc import ABC, abstractmethod
5
6
  from datetime import datetime, timedelta
7
+ from hashlib import new as hashlib_new_method
6
8
  from json.decoder import JSONDecodeError
7
- from pathlib import Path
8
9
  from typing import Any, Dict, List, Optional, Tuple, Union
9
10
 
10
11
  import numpy as np
@@ -12,13 +13,42 @@ import pandas as pd
12
13
  import pytz
13
14
  import requests
14
15
  import urllib3
16
+ from Crypto.Hash import MD4 as _MD4
15
17
  from requests_kerberos import OPTIONAL, HTTPKerberosAuth
18
+ from urllib3.exceptions import InsecureRequestWarning
16
19
 
17
- from tagreader.cache import BaseCache, BucketCache, SmartCache
20
+ from tagreader.cache import BucketCache, SmartCache
18
21
  from tagreader.logger import logger
19
22
  from tagreader.utils import ReaderType, is_mac, is_windows, urljoin
20
23
 
21
24
 
25
+ class MD4:
26
+ def __init__(self, data=None):
27
+ self._hash_obj = _MD4.new()
28
+ if data is not None:
29
+ self._hash_obj.update(data)
30
+
31
+ def update(self, data):
32
+ self._hash_obj.update(data)
33
+
34
+ def digest(self):
35
+ return self._hash_obj.digest()
36
+
37
+ def hexdigest(self):
38
+ return self._hash_obj.hexdigest()
39
+
40
+
41
+ def patched_hashlib_new(name, data=b""):
42
+ if name.lower() == "md4":
43
+ return MD4(data)
44
+ else:
45
+ return hashlib_new_method(name, data)
46
+
47
+
48
+ # Monkey-patch md4 in hashlib.new due to missing support for md4 in later releases of Python:
49
+ hashlib.new = patched_hashlib_new
50
+
51
+
22
52
  def get_verify_ssl() -> Union[bool, str]:
23
53
  if is_windows() or is_mac():
24
54
  return True
@@ -33,13 +63,28 @@ def get_url_pi() -> str:
33
63
  return r"https://piwebapi.equinor.com/piwebapi"
34
64
 
35
65
 
36
- def get_auth_aspen() -> HTTPKerberosAuth:
37
- return HTTPKerberosAuth(mutual_authentication=OPTIONAL)
66
+ def get_auth_aspen(use_internal: bool = True):
67
+ if use_internal:
68
+ return HTTPKerberosAuth(mutual_authentication=OPTIONAL)
69
+
70
+ from msal_bearer.BearerAuth import BearerAuth
71
+
72
+ tenantID = "3aa4a235-b6e2-48d5-9195-7fcf05b459b0"
73
+ clientID = "7adaaa99-897f-428c-8a5f-4053db565b32"
74
+ scopes = [
75
+ "https://ewepwapa1pep04-statoilsrm.msappproxy.net/ProcessExplorer/ProcessData//user_impersonation"
76
+ ]
77
+ return BearerAuth.get_auth(
78
+ tenantID=tenantID, clientID=clientID, scopes=scopes, verbose=True
79
+ )
38
80
 
39
81
 
40
- def get_url_aspen() -> str:
41
- # internal url (redirects to dll)
42
- return r"https://aspenone.api.equinor.com"
82
+ def get_url_aspen(use_internal: bool = False) -> str:
83
+ if use_internal:
84
+ # internal url (redirects to url including AtProcessDataREST.dll)
85
+ return r"https://aspenone.api.equinor.com"
86
+
87
+ return r"https://ewepwapa1pep04-statoilsrm.msappproxy.net/ProcessExplorer/ProcessData/AtProcessDataREST.dll"
43
88
 
44
89
 
45
90
  def list_aspenone_sources(
@@ -57,7 +102,7 @@ def list_aspenone_sources(
57
102
  verify_ssl = get_verify_ssl()
58
103
 
59
104
  if verify_ssl is False:
60
- urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
105
+ urllib3.disable_warnings(InsecureRequestWarning)
61
106
 
62
107
  url_ = urljoin(url, "DataSources")
63
108
  params = {"service": "ProcessData", "allQuotes": 1}
@@ -86,7 +131,7 @@ def list_piwebapi_sources(
86
131
  verify_ssl = get_verify_ssl()
87
132
 
88
133
  if verify_ssl is False:
89
- urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
134
+ urllib3.disable_warnings(InsecureRequestWarning)
90
135
 
91
136
  url_ = urljoin(url, "dataservers")
92
137
  res = requests.get(url_, auth=auth, verify=verify_ssl)
@@ -112,7 +157,7 @@ class BaseHandlerWeb(ABC):
112
157
  self.session = requests.Session()
113
158
  self.session.auth = auth if auth is not None else get_auth_aspen()
114
159
  if verify_ssl is False:
115
- urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
160
+ urllib3.disable_warnings(InsecureRequestWarning)
116
161
  self.session.verify = verify_ssl if verify_ssl is not None else get_verify_ssl()
117
162
 
118
163
  def fetch(
@@ -162,8 +207,7 @@ class BaseHandlerWeb(ABC):
162
207
  ) from None
163
208
 
164
209
  @abstractmethod
165
- def verify_connection(self, datasource: str):
166
- ...
210
+ def verify_connection(self, datasource: str): ...
167
211
 
168
212
 
169
213
  class AspenHandlerWeb(BaseHandlerWeb):
@@ -379,8 +423,24 @@ class AspenHandlerWeb(BaseHandlerWeb):
379
423
  return k
380
424
 
381
425
  def search(
382
- self, tag: Optional[str], desc: Optional[str], timeout: Optional[int] = None
383
- ) -> List[Tuple[str, str]]:
426
+ self,
427
+ tag: Optional[str],
428
+ desc: Optional[str],
429
+ timeout: Optional[int] = None,
430
+ return_desc: bool = True,
431
+ ) -> Union[List[Tuple[str, str]], List[str]]:
432
+ ret = []
433
+
434
+ if tag is None and desc is None:
435
+ raise ValueError("Tag is a required argument")
436
+ # return ret
437
+ elif tag is None and len(desc) == 0:
438
+ raise ValueError("Tag is a required argument")
439
+ # return ret
440
+ elif desc is None and len(tag) == 0:
441
+ raise ValueError("Tag is a required argument")
442
+ # return ret
443
+
384
444
  if tag is None:
385
445
  raise ValueError("Tag is a required argument")
386
446
 
@@ -403,19 +463,24 @@ class AspenHandlerWeb(BaseHandlerWeb):
403
463
  data = self.fetch(url, timeout=timeout)
404
464
 
405
465
  if "tags" not in data["data"]:
406
- return []
466
+ return ret
407
467
 
408
- ret = []
409
468
  for item in data["data"]["tags"]:
410
469
  tagname = item["t"]
411
- description = self._get_tag_description(tagname)
412
- ret.append((tagname, description))
470
+ if not desc and not return_desc:
471
+ ret.append(tagname)
472
+ else:
473
+ description = self._get_tag_description(tagname)
474
+ ret.append((tagname, description))
413
475
 
414
476
  if not desc:
415
- return ret
477
+ pass
478
+ else:
479
+ r = re.compile(desc)
480
+ ret = [x for x in ret if r.search(x[1])]
481
+ if not return_desc:
482
+ ret = [x[0] for x in ret]
416
483
 
417
- r = re.compile(desc)
418
- ret = [x for x in ret if r.search(x[1])]
419
484
  return ret
420
485
 
421
486
  def _get_tag_metadata(self, tag: str):
@@ -791,7 +856,8 @@ class PIHandlerWeb(BaseHandlerWeb):
791
856
  tag: Optional[str] = None,
792
857
  desc: Optional[str] = None,
793
858
  timeout: Optional[int] = None,
794
- ) -> List[Tuple]:
859
+ return_desc: bool = True,
860
+ ) -> Union[List[Tuple[str, str]], List[str]]:
795
861
  params = self.generate_search_query(
796
862
  tag=tag, desc=desc, datasource=self.datasource
797
863
  )
@@ -809,6 +875,10 @@ class PIHandlerWeb(BaseHandlerWeb):
809
875
  params["start"] = next_start # noqa
810
876
  else:
811
877
  done = True
878
+
879
+ if not return_desc:
880
+ ret = [x[0] for x in ret]
881
+
812
882
  return ret
813
883
 
814
884
  def _get_tag_metadata(self, tag: str) -> Dict[str, str]:
File without changes
File without changes
File without changes
File without changes