besapi 3.8.1__tar.gz → 3.8.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
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: besapi
3
- Version: 3.8.1
3
+ Version: 3.8.2
4
4
  Summary: Library for working with the BigFix REST API
5
5
  Home-page: https://github.com/jgstew/besapi
6
6
  Author: Matt Hansen, James Stewart
@@ -8,14 +8,27 @@ Author-email: hansen.m@psu.edu, james@jgstew.com
8
8
  License: MIT
9
9
  Keywords: bigfix iem tem rest api
10
10
  Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
11
12
  Classifier: Operating System :: OS Independent
12
13
  Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Topic :: System :: Systems Administration
13
18
  Requires-Python: >=3.7
14
19
  Description-Content-Type: text/markdown
15
20
  License-File: LICENSE.txt
16
21
  Requires-Dist: requests
17
22
  Requires-Dist: lxml
18
23
  Requires-Dist: cmd2
24
+ Requires-Dist: setuptools
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: home-page
28
+ Dynamic: keywords
29
+ Dynamic: license
30
+ Dynamic: requires-dist
31
+ Dynamic: summary
19
32
 
20
33
  # besapi
21
34
 
@@ -4,8 +4,13 @@ long_description = file: README.md
4
4
  long_description_content_type = text/markdown
5
5
  classifiers =
6
6
  Programming Language :: Python :: 3
7
+ Programming Language :: Python :: 3.9
7
8
  Operating System :: OS Independent
8
9
  License :: OSI Approved :: MIT License
10
+ Development Status :: 5 - Production/Stable
11
+ Intended Audience :: Developers
12
+ Topic :: Software Development :: Libraries :: Python Modules
13
+ Topic :: System :: Systems Administration
9
14
 
10
15
  [options]
11
16
  python_requires = >=3.7
@@ -1,14 +1,9 @@
1
1
  #!/usr/bin/env python
2
- """
3
- file to configure python build and packaging for pip
4
- """
2
+ """File to configure python build and packaging for pip."""
5
3
 
6
- try:
7
- from setuptools import setup
8
- except (ImportError, ModuleNotFoundError):
9
- from distutils.core import setup
4
+ import setuptools
10
5
 
11
- setup(
6
+ setuptools.setup(
12
7
  name="besapi",
13
8
  # version= moved to setup.cfg
14
9
  author="Matt Hansen, James Stewart",
@@ -20,7 +15,7 @@ setup(
20
15
  # long_description= moved to setup.cfg
21
16
  packages=["besapi", "bescli"],
22
17
  package_data={"besapi": ["schemas/*.xsd"]},
23
- install_requires=["requests", "lxml", "cmd2"],
18
+ install_requires=["requests", "lxml", "cmd2", "setuptools"],
24
19
  include_package_data=True,
25
20
  package_dir={"": "src"},
26
21
  )
@@ -1,6 +1,4 @@
1
- """
2
- besapi is a python module for interacting with the BigFix REST API
3
- """
1
+ """Besapi is a python module for interacting with the BigFix REST API."""
4
2
 
5
3
  # https://stackoverflow.com/questions/279237/import-a-module-from-a-relative-path/4397291
6
4
 
@@ -1,6 +1,4 @@
1
- """
2
- To run this module directly
3
- """
1
+ """To run this module directly."""
4
2
 
5
3
  import logging
6
4
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python
2
2
  """
3
- besapi.py
3
+ This module besapi provides a simple interface to the BigFix REST API.
4
4
 
5
5
  MIT License
6
6
  Copyright (c) 2014 Matt Hansen
@@ -23,19 +23,21 @@ import typing
23
23
  try:
24
24
  from urllib import parse
25
25
  except ImportError:
26
- from urlparse import parse_qs as parse
26
+ from urlparse import parse_qs as parse # type: ignore[no-redef,import-not-found]
27
27
 
28
28
  import requests
29
29
  from lxml import etree, objectify
30
30
  from pkg_resources import resource_filename
31
31
 
32
- __version__ = "3.8.1"
32
+ __version__ = "3.8.2"
33
33
 
34
34
  besapi_logger = logging.getLogger("besapi")
35
35
 
36
+ # pylint: disable=consider-using-f-string
37
+
36
38
 
37
39
  def rand_password(length=20):
38
- """get a random password"""
40
+ """Get a random password."""
39
41
 
40
42
  all_safe_chars = string.ascii_letters + string.digits + "!#()*+,-.:;<=>?[]^_|~"
41
43
 
@@ -66,6 +68,7 @@ def sanitize_txt(*args):
66
68
  def elem2dict(node):
67
69
  """
68
70
  Convert an lxml.etree node tree into a dict.
71
+
69
72
  https://gist.github.com/jacobian/795571?permalink_comment_id=2981870#gistcomment-2981870
70
73
  """
71
74
  result = {}
@@ -94,7 +97,10 @@ def elem2dict(node):
94
97
  def replace_text_between(
95
98
  original_text, first_delimiter, second_delimiter, replacement_text
96
99
  ):
97
- """Replace text between delimiters. Each delimiter should only appear once."""
100
+ """Replace text between delimiters.
101
+
102
+ Each delimiter should only appear once.
103
+ """
98
104
  leading_text = original_text.split(first_delimiter)[0]
99
105
  trailing_text = original_text.split(second_delimiter)[1]
100
106
 
@@ -109,7 +115,7 @@ def replace_text_between(
109
115
 
110
116
  # https://github.com/jgstew/generate_bes_from_template/blob/bcc6c79632dd375c2861608ded3ae5872801a669/src/generate_bes_from_template/generate_bes_from_template.py#L87-L92
111
117
  def parse_bes_modtime(string_datetime):
112
- """parse datetime string to object"""
118
+ """Parse datetime string to object."""
113
119
  # ("%a, %d %b %Y %H:%M:%S %z")
114
120
  return datetime.datetime.strptime(string_datetime, "%a, %d %b %Y %H:%M:%S %z")
115
121
 
@@ -153,7 +159,9 @@ def parse_bes_modtime(string_datetime):
153
159
 
154
160
 
155
161
  def get_action_combined_relevance(relevances: typing.List[str]):
156
- """take array of ordered relevance clauses and return relevance string for action"""
162
+ """Take array of ordered relevance clauses and return relevance string for
163
+ action.
164
+ """
157
165
 
158
166
  relevance_combined = ""
159
167
 
@@ -176,7 +184,7 @@ def get_action_combined_relevance(relevances: typing.List[str]):
176
184
 
177
185
 
178
186
  def get_target_xml(targets=None):
179
- """get target xml based upon input
187
+ """Get target xml based upon input.
180
188
 
181
189
  Input can be a single string:
182
190
  - starts with "<AllComputers>" if all computers should be targeted
@@ -239,7 +247,7 @@ def get_target_xml(targets=None):
239
247
 
240
248
 
241
249
  def validate_xsd(doc):
242
- """validate results using XML XSDs"""
250
+ """Validate results using XML XSDs."""
243
251
  try:
244
252
  xmldoc = etree.fromstring(doc)
245
253
  except BaseException: # pylint: disable=broad-except
@@ -262,9 +270,25 @@ def validate_xsd(doc):
262
270
  return False
263
271
 
264
272
 
273
+ def validate_xml_bes_file(file_path):
274
+ """Take a file path as input,
275
+ read as binary data,.
276
+
277
+ validate against xml schema
278
+
279
+ returns True for valid xml
280
+ returns False for invalid xml (or if file is not xml)
281
+ """
282
+ with open(file_path, "rb") as file:
283
+ file_data = file.read()
284
+
285
+ return validate_xsd(file_data)
286
+
287
+
265
288
  def get_bes_conn_using_config_file(conf_file=None):
266
289
  """
267
- read connection values from config file
290
+ Read connection values from config file.
291
+
268
292
  return besapi connection
269
293
  """
270
294
  config_paths = [
@@ -305,7 +329,7 @@ def get_bes_conn_using_config_file(conf_file=None):
305
329
 
306
330
 
307
331
  class BESConnection:
308
- """BigFix RESTAPI connection abstraction class"""
332
+ """BigFix RESTAPI connection abstraction class."""
309
333
 
310
334
  def __init__(self, username, password, rootserver, verify=False):
311
335
  if not verify:
@@ -341,7 +365,7 @@ class BESConnection:
341
365
  self.login()
342
366
 
343
367
  def __repr__(self):
344
- """object representation"""
368
+ """Object representation."""
345
369
  # https://stackoverflow.com/a/2626364/861745
346
370
  return f"Object: besapi.BESConnection( username={self.username}, rootserver={self.rootserver} )"
347
371
 
@@ -355,16 +379,16 @@ class BESConnection:
355
379
  return False
356
380
 
357
381
  def __del__(self):
358
- """cleanup on deletion of instance"""
382
+ """Cleanup on deletion of instance."""
359
383
  self.logout()
360
384
  self.session.auth = None
361
385
 
362
386
  def __bool__(self):
363
- """get true or false"""
387
+ """Get true or false."""
364
388
  return self.login()
365
389
 
366
390
  def url(self, path):
367
- """get absolute url"""
391
+ """Get absolute url."""
368
392
  if path.startswith(self.rootserver):
369
393
  url = path
370
394
  else:
@@ -373,35 +397,61 @@ class BESConnection:
373
397
  return url
374
398
 
375
399
  def get(self, path="help", **kwargs):
376
- """HTTP GET request"""
400
+ """HTTP GET request."""
377
401
  self.last_connected = datetime.datetime.now()
378
402
  return RESTResult(
379
403
  self.session.get(self.url(path), verify=self.verify, **kwargs)
380
404
  )
381
405
 
382
- def post(self, path, data, **kwargs):
383
- """HTTP POST request"""
406
+ def post(self, path, data, validate_xml=None, **kwargs):
407
+ """HTTP POST request."""
408
+
409
+ # if validate_xml is true, data must validate to xml schema
410
+ # if validate_xml is false, no schema check will be made
411
+ if validate_xml:
412
+ if not validate_xsd(data):
413
+ err_msg = "data being posted did not validate to XML schema. If expected, consider setting validate_xml to false."
414
+ if validate_xml:
415
+ logging.error(err_msg)
416
+ raise ValueError(err_msg)
417
+ else:
418
+ # this is intended it validate_xml is None, but not used currently
419
+ logging.warning(err_msg)
420
+
384
421
  self.last_connected = datetime.datetime.now()
385
422
  return RESTResult(
386
423
  self.session.post(self.url(path), data=data, verify=self.verify, **kwargs)
387
424
  )
388
425
 
389
- def put(self, path, data, **kwargs):
390
- """HTTP PUT request"""
426
+ def put(self, path, data, validate_xml=None, **kwargs):
427
+ """HTTP PUT request."""
391
428
  self.last_connected = datetime.datetime.now()
429
+
430
+ # if validate_xml is true, data must validate to xml schema
431
+ # if validate_xml is false, no schema check will be made
432
+ if validate_xml:
433
+ if not validate_xsd(data):
434
+ err_msg = "data being put did not validate to XML schema. If expected, consider setting validate_xml to false."
435
+ if validate_xml:
436
+ logging.error(err_msg)
437
+ raise ValueError(err_msg)
438
+ else:
439
+ # this is intended it validate_xml is None, but not used currently
440
+ logging.warning(err_msg)
441
+
392
442
  return RESTResult(
393
443
  self.session.put(self.url(path), data=data, verify=self.verify, **kwargs)
394
444
  )
395
445
 
396
446
  def delete(self, path, **kwargs):
397
- """HTTP DELETE request"""
447
+ """HTTP DELETE request."""
398
448
  self.last_connected = datetime.datetime.now()
399
449
  return RESTResult(
400
450
  self.session.delete(self.url(path), verify=self.verify, **kwargs)
401
451
  )
402
452
 
403
453
  def session_relevance_xml(self, relevance, **kwargs):
404
- """Get Session Relevance Results XML"""
454
+ """Get Session Relevance Results XML."""
405
455
  self.last_connected = datetime.datetime.now()
406
456
  return RESTResult(
407
457
  self.session.post(
@@ -413,7 +463,7 @@ class BESConnection:
413
463
  )
414
464
 
415
465
  def session_relevance_array(self, relevance, **kwargs):
416
- """Get Session Relevance Results array"""
466
+ """Get Session Relevance Results array."""
417
467
  rel_result = self.session_relevance_xml(relevance, **kwargs)
418
468
  # print(rel_result)
419
469
  result = []
@@ -440,14 +490,14 @@ class BESConnection:
440
490
  return result
441
491
 
442
492
  def session_relevance_string(self, relevance, **kwargs):
443
- """Get Session Relevance Results string"""
493
+ """Get Session Relevance Results string."""
444
494
  rel_result_array = self.session_relevance_array(
445
495
  "(it as string) of ( " + relevance + " )", **kwargs
446
496
  )
447
497
  return "\n".join(rel_result_array)
448
498
 
449
499
  def login(self):
450
- """do login"""
500
+ """Do login."""
451
501
  if bool(self.last_connected):
452
502
  duration_obj = datetime.datetime.now() - self.last_connected
453
503
  duration_minutes = duration_obj / datetime.timedelta(minutes=1)
@@ -478,14 +528,14 @@ class BESConnection:
478
528
  return bool(self.last_connected)
479
529
 
480
530
  def logout(self):
481
- """clear session and close it"""
531
+ """Clear session and close it."""
482
532
  self.session.cookies.clear()
483
533
  self.session.close()
484
534
 
485
535
  def set_dashboard_variable_value(
486
536
  self, dashboard_name, var_name, var_value, private=False
487
537
  ):
488
- """set the variable value from a dashboard datastore"""
538
+ """Set the variable value from a dashboard datastore."""
489
539
 
490
540
  dash_var_xml = f"""<BESAPI xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="BESAPI.xsd">
491
541
  <DashboardData>
@@ -502,7 +552,7 @@ class BESConnection:
502
552
  )
503
553
 
504
554
  def get_dashboard_variable_value(self, dashboard_name, var_name):
505
- """get the variable value from a dashboard datastore"""
555
+ """Get the variable value from a dashboard datastore."""
506
556
 
507
557
  return str(
508
558
  self.get(
@@ -511,7 +561,7 @@ class BESConnection:
511
561
  )
512
562
 
513
563
  def validate_site_path(self, site_path, check_site_exists=True, raise_error=False):
514
- """make sure site_path is valid"""
564
+ """Make sure site_path is valid."""
515
565
 
516
566
  if site_path is None:
517
567
  if not raise_error:
@@ -554,8 +604,9 @@ class BESConnection:
554
604
  )
555
605
 
556
606
  def get_current_site_path(self, site_path=None):
557
- """if site_path is none, get current instance site_path,
558
- otherwise validate and return provided site_path"""
607
+ """If site_path is none, get current instance site_path,
608
+ otherwise validate and return provided site_path.
609
+ """
559
610
 
560
611
  # use instance site_path context if none provided:
561
612
  if site_path is None or str(site_path).strip() == "":
@@ -569,14 +620,14 @@ class BESConnection:
569
620
  return self.validate_site_path(site_path, check_site_exists=False)
570
621
 
571
622
  def set_current_site_path(self, site_path):
572
- """set current site path context"""
623
+ """Set current site path context."""
573
624
 
574
625
  if self.validate_site_path(site_path):
575
626
  self.site_path = site_path
576
627
  return self.site_path
577
628
 
578
629
  def import_bes_to_site(self, bes_file_path, site_path=None):
579
- """import bes file to site"""
630
+ """Import bes file to site."""
580
631
 
581
632
  if not os.access(bes_file_path, os.R_OK):
582
633
  besapi_logger.error("%s is not readable", bes_file_path)
@@ -599,7 +650,7 @@ class BESConnection:
599
650
  return result
600
651
 
601
652
  def create_site_from_file(self, bes_file_path, site_type="custom"):
602
- """create new site"""
653
+ """Create new site."""
603
654
  xml_parsed = etree.parse(bes_file_path)
604
655
  new_site_name = xml_parsed.xpath("/BES/CustomSite/Name/text()")[0]
605
656
 
@@ -616,7 +667,7 @@ class BESConnection:
616
667
  return result_site
617
668
 
618
669
  def get_user(self, user_name):
619
- """get a user"""
670
+ """Get a user."""
620
671
 
621
672
  result_users = self.get(f"operator/{user_name}")
622
673
 
@@ -626,7 +677,7 @@ class BESConnection:
626
677
  besapi_logger.info("User `%s` Not Found!", user_name)
627
678
 
628
679
  def create_user_from_file(self, bes_file_path):
629
- """create user from xml"""
680
+ """Create user from xml."""
630
681
  xml_parsed = etree.parse(bes_file_path)
631
682
  new_user_name = xml_parsed.xpath("/BESAPI/Operator/Name/text()")[0]
632
683
  result_user = self.get_user(new_user_name)
@@ -640,7 +691,7 @@ class BESConnection:
640
691
  return self.get_user(new_user_name)
641
692
 
642
693
  def get_computergroup(self, group_name, site_path=None):
643
- """get computer group resource URI"""
694
+ """Get computer group resource URI."""
644
695
 
645
696
  site_path = self.get_current_site_path(site_path)
646
697
  result_groups = self.get(f"computergroups/{site_path}")
@@ -655,7 +706,7 @@ class BESConnection:
655
706
  besapi_logger.info("Group `%s` Not Found!", group_name)
656
707
 
657
708
  def create_group_from_file(self, bes_file_path, site_path=None):
658
- """create a new group"""
709
+ """Create a new group."""
659
710
  site_path = self.get_current_site_path(site_path)
660
711
  xml_parsed = etree.parse(bes_file_path)
661
712
  new_group_name = xml_parsed.xpath("/BES/ComputerGroup/Title/text()")[0]
@@ -674,7 +725,7 @@ class BESConnection:
674
725
 
675
726
  def get_upload(self, file_name, file_hash):
676
727
  """
677
- check for a specific file upload reference
728
+ Check for a specific file upload reference.
678
729
 
679
730
  each upload is uniquely identified by sha1 and filename
680
731
 
@@ -700,7 +751,8 @@ class BESConnection:
700
751
 
701
752
  def upload(self, file_path, file_name=None, file_hash=None):
702
753
  """
703
- upload a single file
754
+ Upload a single file.
755
+
704
756
  https://developer.bigfix.com/rest-api/api/upload.html
705
757
  """
706
758
  if not os.access(file_path, os.R_OK):
@@ -751,7 +803,7 @@ class BESConnection:
751
803
  def parse_upload_result_to_prefetch(
752
804
  self, result_upload, use_localhost=True, use_https=True
753
805
  ):
754
- """take a rest response from an upload and parse into prefetch"""
806
+ """Take a rest response from an upload and parse into prefetch."""
755
807
  file_url = str(result_upload.besobj.FileUpload.URL)
756
808
  if use_https:
757
809
  file_url = file_url.replace("http://", "https://")
@@ -772,7 +824,7 @@ class BESConnection:
772
824
  return f"prefetch {file_name} sha1:{file_sha1} size:{file_size} {file_url} sha256:{file_sha256}"
773
825
 
774
826
  def get_content_by_resource(self, resource_url):
775
- """get a single content item by resource"""
827
+ """Get a single content item by resource."""
776
828
  # Get Specific Content
777
829
  content = None
778
830
  try:
@@ -790,7 +842,7 @@ class BESConnection:
790
842
  return content
791
843
 
792
844
  def update_item_from_file(self, file_path, site_path=None):
793
- """update an item by name and last modified"""
845
+ """Update an item by name and last modified."""
794
846
  site_path = self.get_current_site_path(site_path)
795
847
  bes_tree = etree.parse(file_path)
796
848
 
@@ -823,7 +875,7 @@ class BESConnection:
823
875
  export_folder="./",
824
876
  name_trim=100,
825
877
  ):
826
- """save an xml string to bes file"""
878
+ """Save an xml string to bes file."""
827
879
  item_folder = export_folder
828
880
  if not os.path.exists(item_folder):
829
881
  os.makedirs(item_folder)
@@ -851,8 +903,10 @@ class BESConnection:
851
903
  include_item_type_folder=False,
852
904
  include_item_id=False,
853
905
  ):
854
- """export a single item by resource
906
+ """Export a single item by resource.
907
+
855
908
  example resources:
909
+
856
910
  - content_type/site_type/site/id
857
911
  - https://localhost:52311/api/content_type/site_type/site/id
858
912
  """
@@ -899,8 +953,10 @@ class BESConnection:
899
953
  include_site_folder=True,
900
954
  include_item_ids=True,
901
955
  ):
902
- """export contents of site
956
+ """Export contents of site.
957
+
903
958
  Originally here:
959
+
904
960
  - https://gist.github.com/jgstew/1b2da12af59b71c9f88a
905
961
  - https://bigfix.me/fixlet/details/21282
906
962
  """
@@ -976,7 +1032,7 @@ class BESConnection:
976
1032
  def export_all_sites(
977
1033
  self, include_external=False, export_folder="./", name_trim=70, verbose=False
978
1034
  ):
979
- """export all bigfix sites to a folder"""
1035
+ """Export all bigfix sites to a folder."""
980
1036
  results_sites = self.get("sites")
981
1037
  if verbose:
982
1038
  print(results_sites)
@@ -995,11 +1051,12 @@ class BESConnection:
995
1051
 
996
1052
 
997
1053
  class RESTResult:
998
- """BigFix REST API Result Abstraction Class"""
1054
+ """BigFix REST API Result Abstraction Class."""
999
1055
 
1000
1056
  def __init__(self, request):
1001
1057
  self.request = request
1002
1058
  self.text = request.text
1059
+ self.valid = None
1003
1060
  self._besxml = None
1004
1061
  self._besobj = None
1005
1062
  self._besdict = None
@@ -1034,7 +1091,9 @@ class RESTResult:
1034
1091
  if self.validate_xsd(request.text):
1035
1092
  self.valid = True
1036
1093
  else:
1037
- # print("WARNING: response appears invalid")
1094
+ logging.debug(
1095
+ "INFO: REST API Result does not appear to be XML, this could be expected."
1096
+ )
1038
1097
  self.valid = False
1039
1098
 
1040
1099
  def __str__(self):
@@ -1052,7 +1111,7 @@ class RESTResult:
1052
1111
 
1053
1112
  @property
1054
1113
  def besxml(self):
1055
- """property for parsed xml representation"""
1114
+ """Property for parsed xml representation."""
1056
1115
  if self.valid and self._besxml is None:
1057
1116
  self._besxml = self.xmlparse_text(self.text)
1058
1117
 
@@ -1060,7 +1119,7 @@ class RESTResult:
1060
1119
 
1061
1120
  @property
1062
1121
  def besobj(self):
1063
- """property for xml object representation"""
1122
+ """Property for xml object representation."""
1064
1123
  if self.valid and self._besobj is None:
1065
1124
  self._besobj = self.objectify_text(self.text)
1066
1125
 
@@ -1068,7 +1127,7 @@ class RESTResult:
1068
1127
 
1069
1128
  @property
1070
1129
  def besdict(self):
1071
- """property for python dict representation"""
1130
+ """Property for python dict representation."""
1072
1131
  if self._besdict is None:
1073
1132
  if self.valid:
1074
1133
  self._besdict = elem2dict(etree.fromstring(self.besxml))
@@ -1079,18 +1138,21 @@ class RESTResult:
1079
1138
 
1080
1139
  @property
1081
1140
  def besjson(self):
1082
- """property for json representation"""
1141
+ """Property for json representation."""
1083
1142
  if self._besjson is None:
1084
1143
  self._besjson = json.dumps(self.besdict, indent=2)
1085
1144
 
1086
1145
  return self._besjson
1087
1146
 
1088
1147
  def validate_xsd(self, doc):
1089
- """validate results using XML XSDs"""
1148
+ """Validate results using XML XSDs."""
1149
+ # return self.valid if already set
1150
+ if self.valid is not None and isinstance(self.valid, bool):
1151
+ return self.valid
1090
1152
  return validate_xsd(doc)
1091
1153
 
1092
1154
  def xmlparse_text(self, text):
1093
- """parse response text as xml"""
1155
+ """Parse response text as xml."""
1094
1156
  if type(text) is str:
1095
1157
  root_xml = etree.fromstring(text.encode("utf-8"))
1096
1158
  else:
@@ -1099,7 +1161,7 @@ class RESTResult:
1099
1161
  return etree.tostring(root_xml, encoding="utf-8", xml_declaration=True)
1100
1162
 
1101
1163
  def objectify_text(self, text):
1102
- """parse response text as objectified xml"""
1164
+ """Parse response text as objectified xml."""
1103
1165
  if type(text) is str:
1104
1166
  root_xml = text.encode("utf-8")
1105
1167
  else:
@@ -1109,7 +1171,7 @@ class RESTResult:
1109
1171
 
1110
1172
 
1111
1173
  def main():
1112
- """if invoked directly, run bescli command loop"""
1174
+ """If invoked directly, run bescli command loop."""
1113
1175
  # pylint: disable=import-outside-toplevel
1114
1176
  try:
1115
1177
  from bescli import bescli
@@ -1,4 +1,4 @@
1
- """This is a set of utility functions for use in multiple plugins
1
+ """This is a set of utility functions for use in multiple plugins.
2
2
 
3
3
  see example here: https://github.com/jgstew/besapi/blob/master/examples/export_all_sites.py
4
4
  """
@@ -16,7 +16,7 @@ import besapi
16
16
 
17
17
  # NOTE: This does not work as expected when run from plugin_utilities
18
18
  def get_invoke_folder(verbose=0):
19
- """Get the folder the script was invoked from"""
19
+ """Get the folder the script was invoked from."""
20
20
  # using logging here won't actually log it to the file:
21
21
 
22
22
  if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
@@ -36,7 +36,7 @@ def get_invoke_folder(verbose=0):
36
36
 
37
37
  # NOTE: This does not work as expected when run from plugin_utilities
38
38
  def get_invoke_file_name(verbose=0):
39
- """Get the filename the script was invoked from"""
39
+ """Get the filename the script was invoked from."""
40
40
  # using logging here won't actually log it to the file:
41
41
 
42
42
  if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
@@ -56,7 +56,7 @@ def get_invoke_file_name(verbose=0):
56
56
 
57
57
 
58
58
  def setup_plugin_argparse(plugin_args_required=False):
59
- """setup argparse for plugin use"""
59
+ """Setup argparse for plugin use."""
60
60
  arg_parser = argparse.ArgumentParser(
61
61
  description="Provide command line arguments for REST URL, username, and password"
62
62
  )
@@ -92,9 +92,10 @@ def setup_plugin_argparse(plugin_args_required=False):
92
92
 
93
93
 
94
94
  def get_plugin_logging_config(log_file_path="", verbose=0, console=True):
95
- """get config for logging for plugin use
95
+ """Get config for logging for plugin use.
96
96
 
97
- use this like: logging.basicConfig(**logging_config)"""
97
+ use this like: logging.basicConfig(**logging_config)
98
+ """
98
99
 
99
100
  if not log_file_path or log_file_path == "":
100
101
  log_file_path = os.path.join(
@@ -131,7 +132,9 @@ def get_plugin_logging_config(log_file_path="", verbose=0, console=True):
131
132
 
132
133
 
133
134
  def get_besapi_connection(args):
134
- """get connection to besapi using either args or config file if args not provided"""
135
+ """Get connection to besapi using either args or config file if args not
136
+ provided.
137
+ """
135
138
 
136
139
  password = args.password
137
140
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: besapi
3
- Version: 3.8.1
3
+ Version: 3.8.2
4
4
  Summary: Library for working with the BigFix REST API
5
5
  Home-page: https://github.com/jgstew/besapi
6
6
  Author: Matt Hansen, James Stewart
@@ -8,14 +8,27 @@ Author-email: hansen.m@psu.edu, james@jgstew.com
8
8
  License: MIT
9
9
  Keywords: bigfix iem tem rest api
10
10
  Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
11
12
  Classifier: Operating System :: OS Independent
12
13
  Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Topic :: System :: Systems Administration
13
18
  Requires-Python: >=3.7
14
19
  Description-Content-Type: text/markdown
15
20
  License-File: LICENSE.txt
16
21
  Requires-Dist: requests
17
22
  Requires-Dist: lxml
18
23
  Requires-Dist: cmd2
24
+ Requires-Dist: setuptools
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: home-page
28
+ Dynamic: keywords
29
+ Dynamic: license
30
+ Dynamic: requires-dist
31
+ Dynamic: summary
19
32
 
20
33
  # besapi
21
34
 
@@ -1,3 +1,4 @@
1
1
  requests
2
2
  lxml
3
3
  cmd2
4
+ setuptools
@@ -1,6 +1,4 @@
1
- """
2
- bescli provides a command line interface to interact with besapi
3
- """
1
+ """This bescli provides a command line interface to interact with besapi."""
4
2
 
5
3
  # https://stackoverflow.com/questions/279237/import-a-module-from-a-relative-path/4397291
6
4
 
@@ -1,6 +1,4 @@
1
- """
2
- To run this module directly
3
- """
1
+ """To run this module directly."""
4
2
 
5
3
  import logging
6
4
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python
2
2
  """
3
- bescli.py
3
+ This bescli module provides a command line interface to interact with besapi.
4
4
 
5
5
  MIT License
6
6
  Copyright (c) 2014 Matt Hansen
@@ -27,12 +27,12 @@ except ModuleNotFoundError:
27
27
  from besapi import besapi
28
28
  except ImportError:
29
29
  # this is for the case in which we are calling bescli from besapi
30
- import besapi
30
+ import besapi # type: ignore[no-redef]
31
31
 
32
32
  try:
33
33
  from besapi.besapi import __version__
34
34
  except ImportError:
35
- from besapi import __version__
35
+ from besapi import __version__ # type: ignore
36
36
 
37
37
 
38
38
  class BESCLInterface(Cmd):
@@ -62,7 +62,7 @@ class BESCLInterface(Cmd):
62
62
  self.do_conf()
63
63
 
64
64
  def parse_help_resources(self):
65
- """get api resources from help"""
65
+ """Get api resources from help."""
66
66
  if self.bes_conn:
67
67
  help_result = self.bes_conn.get("help")
68
68
  help_result = help_result.text.split("\n")
@@ -89,7 +89,7 @@ class BESCLInterface(Cmd):
89
89
  ]
90
90
 
91
91
  def complete_api_resources(self, text, line, begidx, endidx):
92
- """define completion for apis"""
92
+ """Define completion for apis."""
93
93
 
94
94
  # only initialize once
95
95
  if not self.api_resources:
@@ -102,7 +102,9 @@ class BESCLInterface(Cmd):
102
102
  complete_get = complete_api_resources
103
103
 
104
104
  def do_get(self, line):
105
- """Perform get request to BigFix server using provided api endpoint argument"""
105
+ """Perform get request to BigFix server using provided api endpoint
106
+ argument.
107
+ """
106
108
 
107
109
  # remove any extra whitespace
108
110
  line = line.strip()
@@ -136,7 +138,9 @@ class BESCLInterface(Cmd):
136
138
  complete_delete = complete_api_resources
137
139
 
138
140
  def do_delete(self, line):
139
- """Perform delete request to BigFix server using provided api endpoint argument"""
141
+ """Perform delete request to BigFix server using provided api endpoint
142
+ argument.
143
+ """
140
144
 
141
145
  # remove any extra whitespace
142
146
  line = line.strip()
@@ -158,20 +162,20 @@ class BESCLInterface(Cmd):
158
162
  complete_post = complete_api_resources
159
163
 
160
164
  def do_post(self, statement):
161
- """post file as data to path"""
165
+ """Post file as data to path."""
162
166
  self.poutput(statement)
163
167
  self.poutput("not yet implemented")
164
168
 
165
169
  def do_config(self, conf_file=None):
166
- """Attempt to load config info from file and login"""
170
+ """Attempt to load config info from file and login."""
167
171
  self.do_conf(conf_file)
168
172
 
169
173
  def do_loadconfig(self, conf_file=None):
170
- """Attempt to load config info from file and login"""
174
+ """Attempt to load config info from file and login."""
171
175
  self.do_conf(conf_file)
172
176
 
173
177
  def do_conf(self, conf_file=None):
174
- """Attempt to load config info from file and login"""
178
+ """Attempt to load config info from file and login."""
175
179
  config_path = [
176
180
  "/etc/besapi.conf",
177
181
  os.path.expanduser("~/besapi.conf"),
@@ -213,7 +217,7 @@ class BESCLInterface(Cmd):
213
217
  self.do_login()
214
218
 
215
219
  def do_login(self, user=None):
216
- """Login to BigFix Server"""
220
+ """Login to BigFix Server."""
217
221
 
218
222
  if not user:
219
223
  if self.BES_USER_NAME:
@@ -292,7 +296,7 @@ class BESCLInterface(Cmd):
292
296
  self.perror("Login Error!")
293
297
 
294
298
  def do_logout(self, _=None):
295
- """Logout and clear session"""
299
+ """Logout and clear session."""
296
300
  if self.bes_conn:
297
301
  self.bes_conn.logout()
298
302
  # del self.bes_conn
@@ -300,7 +304,7 @@ class BESCLInterface(Cmd):
300
304
  self.pfeedback("Logout Complete!")
301
305
 
302
306
  def do_debug(self, setting):
303
- """Enable or Disable Debug Mode"""
307
+ """Enable or Disable Debug Mode."""
304
308
  self.poutput(bool(setting))
305
309
  self.debug = bool(setting)
306
310
  self.echo = bool(setting)
@@ -312,7 +316,7 @@ class BESCLInterface(Cmd):
312
316
  logging.getLogger("besapi").setLevel(logging.WARNING)
313
317
 
314
318
  def do_clear(self, arg=None):
315
- """clear current config and logout"""
319
+ """Clear current config and logout."""
316
320
  if self.bes_conn:
317
321
  self.bes_conn.logout()
318
322
  # self.bes_conn = None
@@ -332,11 +336,11 @@ class BESCLInterface(Cmd):
332
336
  self.BES_PASSWORD = None
333
337
 
334
338
  def do_saveconfig(self, arg=None):
335
- """save current config to file"""
339
+ """Save current config to file."""
336
340
  self.do_saveconf(arg)
337
341
 
338
342
  def do_saveconf(self, _=None):
339
- """save current config to file"""
343
+ """Save current config to file."""
340
344
  if not self.bes_conn:
341
345
  self.do_login()
342
346
  if not self.bes_conn:
@@ -348,11 +352,11 @@ class BESCLInterface(Cmd):
348
352
  self.CONFPARSER.write(configfile)
349
353
 
350
354
  def do_showconfig(self, arg=None):
351
- """List the current settings and connection status"""
355
+ """List the current settings and connection status."""
352
356
  self.do_ls(arg)
353
357
 
354
358
  def do_ls(self, _=None):
355
- """List the current settings and connection status"""
359
+ """List the current settings and connection status."""
356
360
  self.poutput(" Connected: " + str(bool(self.bes_conn)))
357
361
  self.poutput(
358
362
  " BES_ROOT_SERVER: "
@@ -372,17 +376,17 @@ class BESCLInterface(Cmd):
372
376
  )
373
377
 
374
378
  def do_error_count(self, _=None):
375
- """Output the number of errors"""
379
+ """Output the number of errors."""
376
380
  self.poutput(f"Error Count: {self.num_errors}")
377
381
 
378
382
  def do_exit(self, _=None):
379
- """Exit this application"""
383
+ """Exit this application."""
380
384
  self.exit_code = self.num_errors
381
385
  # no matter what I try I can't get anything but exit code 0 on windows
382
386
  return self.do_quit("")
383
387
 
384
388
  def do_query(self, statement):
385
- """Get Session Relevance Results"""
389
+ """Get Session Relevance Results."""
386
390
  if not self.bes_conn:
387
391
  self.do_login()
388
392
  if not self.bes_conn:
@@ -397,53 +401,53 @@ class BESCLInterface(Cmd):
397
401
  self.poutput(rel_result)
398
402
 
399
403
  def do_version(self, _=None):
400
- """output version of besapi"""
404
+ """Output version of besapi."""
401
405
  self.poutput(f"besapi version: {__version__}")
402
406
 
403
407
  def do_get_action(self, statement=None):
404
- """usage: get_action 123"""
408
+ """Usage: get_action 123."""
405
409
  result_op = self.bes_conn.get(f"action/{statement}")
406
410
  self.poutput(result_op)
407
411
 
408
412
  def do_get_operator(self, statement=None):
409
- """usage: get_operator ExampleOperatorName"""
413
+ """Usage: get_operator ExampleOperatorName."""
410
414
  result_op = self.bes_conn.get_user(statement)
411
415
  self.poutput(result_op)
412
416
 
413
417
  def do_get_current_site(self, _=None):
414
- """output current site path context"""
418
+ """Output current site path context."""
415
419
  self.poutput(
416
- f"Current Site Path: `{ self.bes_conn.get_current_site_path(None) }`"
420
+ f"Current Site Path: `{self.bes_conn.get_current_site_path(None)}`"
417
421
  )
418
422
 
419
423
  def do_set_current_site(self, statement=None):
420
- """set current site path context"""
424
+ """Set current site path context."""
421
425
  self.poutput(
422
- f"New Site Path: `{ self.bes_conn.set_current_site_path(statement) }`"
426
+ f"New Site Path: `{self.bes_conn.set_current_site_path(statement)}`"
423
427
  )
424
428
 
425
429
  def do_get_content(self, resource_url):
426
- """get a specific item by resource url"""
430
+ """Get a specific item by resource url."""
427
431
  self.poutput(self.bes_conn.get_content_by_resource(resource_url))
428
432
 
429
433
  def do_export_item_by_resource(self, statement):
430
- """export content itemb to current folder"""
434
+ """Export content itemb to current folder."""
431
435
  self.poutput(self.bes_conn.export_item_by_resource(statement))
432
436
 
433
437
  def do_export_site(self, site_path):
434
- """export site contents to current folder"""
438
+ """Export site contents to current folder."""
435
439
  self.bes_conn.export_site_contents(
436
440
  site_path, verbose=True, include_site_folder=False, include_item_ids=False
437
441
  )
438
442
 
439
443
  def do_export_all_sites(self, _=None):
440
- """export site contents to current folder"""
444
+ """Export site contents to current folder."""
441
445
  self.bes_conn.export_all_sites(verbose=False)
442
446
 
443
447
  complete_import_bes = Cmd.path_complete
444
448
 
445
449
  def do_import_bes(self, statement):
446
- """import bes file"""
450
+ """Import bes file."""
447
451
 
448
452
  bes_file_path = str(statement.args).strip()
449
453
 
@@ -456,7 +460,7 @@ class BESCLInterface(Cmd):
456
460
  complete_upload = Cmd.path_complete
457
461
 
458
462
  def do_upload(self, file_path):
459
- """upload file to root server"""
463
+ """Upload file to root server."""
460
464
  if not os.access(file_path, os.R_OK):
461
465
  self.poutput(file_path, "is not a readable file")
462
466
  else:
@@ -467,7 +471,7 @@ class BESCLInterface(Cmd):
467
471
  complete_create_group = Cmd.path_complete
468
472
 
469
473
  def do_create_group(self, file_path):
470
- """create bigfix group from bes file"""
474
+ """Create bigfix group from bes file."""
471
475
  if not os.access(file_path, os.R_OK):
472
476
  self.poutput(file_path, "is not a readable file")
473
477
  else:
@@ -476,7 +480,7 @@ class BESCLInterface(Cmd):
476
480
  complete_create_user = Cmd.path_complete
477
481
 
478
482
  def do_create_user(self, file_path):
479
- """create bigfix user from bes file"""
483
+ """Create bigfix user from bes file."""
480
484
  if not os.access(file_path, os.R_OK):
481
485
  self.poutput(file_path, "is not a readable file")
482
486
  else:
@@ -485,7 +489,7 @@ class BESCLInterface(Cmd):
485
489
  complete_create_site = Cmd.path_complete
486
490
 
487
491
  def do_create_site(self, file_path):
488
- """create bigfix site from bes file"""
492
+ """Create bigfix site from bes file."""
489
493
  if not os.access(file_path, os.R_OK):
490
494
  self.poutput(file_path, "is not a readable file")
491
495
  else:
@@ -494,14 +498,14 @@ class BESCLInterface(Cmd):
494
498
  complete_update_item = Cmd.path_complete
495
499
 
496
500
  def do_update_item(self, file_path):
497
- """update bigfix content item from bes file"""
501
+ """Update bigfix content item from bes file."""
498
502
  if not os.access(file_path, os.R_OK):
499
503
  self.poutput(file_path, "is not a readable file")
500
504
  else:
501
505
  self.poutput(self.bes_conn.update_item_from_file(file_path))
502
506
 
503
507
  def do_serverinfo(self, _=None):
504
- """get server info and return formatted"""
508
+ """Get server info and return formatted."""
505
509
 
506
510
  # not sure what the minimum version for this is:
507
511
  result = self.bes_conn.get("serverinfo")
@@ -513,7 +517,7 @@ class BESCLInterface(Cmd):
513
517
 
514
518
 
515
519
  def main():
516
- """Run the command loop if invoked"""
520
+ """Run the command loop if invoked."""
517
521
  BESCLInterface().cmdloop()
518
522
 
519
523
 
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env python
2
- """
3
- Test besapi
4
- """
2
+ """Test besapi."""
5
3
 
6
4
  import argparse
7
5
  import os
@@ -9,6 +7,8 @@ import random
9
7
  import subprocess
10
8
  import sys
11
9
 
10
+ # mypy: disable-error-code="arg-type"
11
+
12
12
  # check for --test_pip arg
13
13
  parser = argparse.ArgumentParser()
14
14
  parser.add_argument(
@@ -79,7 +79,7 @@ assert raised_errors == 4
79
79
 
80
80
  class RequestResult(object):
81
81
  text = "this is just a test"
82
- headers = []
82
+ headers = [] # type: ignore
83
83
 
84
84
 
85
85
  request_result = RequestResult()
File without changes
File without changes
File without changes
File without changes