From e2304a5ca53bbf447517398ce506a0b9c51ab39c Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Fri, 6 May 2022 21:26:44 +0530 Subject: [PATCH 1/5] fix: remove default logging in dicttoxml Library shouldn't be emitting logs by default and not pollute the consumers. Hence, we put the LOGS behind a flag. To see the library's logs, set DEBUGMODE=True in environment for the logs to work. DEBUGMODE is a fairly unique env variable so should do do the trick. - Github Issue: #130 Reported-by: https://github.com/ricciarellif Authored-by: Vinit Kumar Signed-off-by: Vinit Kumar --- json2xml/dicttoxml.py | 109 +++++++++++++++++++++++------------------ tests/test_json2xml.py | 11 ----- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/json2xml/dicttoxml.py b/json2xml/dicttoxml.py index 896c1ef2..1a5f2560 100755 --- a/json2xml/dicttoxml.py +++ b/json2xml/dicttoxml.py @@ -11,7 +11,7 @@ Items with a `None` type become empty XML elements. This module works with Python 3.7+ """ - +import os import datetime import logging import numbers @@ -21,6 +21,7 @@ from defusedxml.minidom import parseString +DEBUGMODE = os.getenv("DEBUGMODE") LOG = logging.getLogger("dicttoxml") @@ -100,7 +101,8 @@ def make_attrstring(attr: dict[str, Any]) -> str: def key_is_valid_xml(key: str) -> bool: """Checks that a key is a valid XML name""" - LOG.info(f'Inside key_is_valid_xml(). Testing "{str(key)}"') + if DEBUGMODE: + LOG.info(f'Inside key_is_valid_xml(). Testing "{str(key)}"') test_xml = f'<{key}>foo' try: parseString(test_xml) @@ -111,9 +113,10 @@ def key_is_valid_xml(key: str) -> bool: def make_valid_xml_name(key: str, attr: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: """Tests an XML name and fixes it if invalid""" - LOG.info( - f'Inside make_valid_xml_name(). Testing key "{str(key)}" with attr "{str(attr)}"' - ) + if DEBUGMODE: + LOG.info( + f'Inside make_valid_xml_name(). Testing key "{str(key)}" with attr "{str(attr)}"' + ) key = escape_xml(key) # nothing happens at escape_xml if attr is not a string, we don't # need to pass it to the method at all. @@ -162,10 +165,11 @@ def convert( ) -> str: """Routes the elements of an object to the right function to convert them based on their data type""" - LOG.info(f'Inside convert(). type(obj)="{type(obj).__name__}"') - # avoid cpu consuming object serialization => extra if - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' obj="{str(obj)}"') + if DEBUGMODE: + LOG.info(f'Inside convert(). type(obj)="{type(obj).__name__}"') + # avoid cpu consuming object serialization => extra if + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' obj="{str(obj)}"') item_name = item_func(parent) # since bool is also a subtype of number.Number and int, the check for bool @@ -227,12 +231,13 @@ def dict2xml_str( parse dict2xml """ keys_str = ", ".join(str(key) for key in item) - LOG.info( - f'Inside dict_item2xml_str: type(obj)="{type(item).__name__}", keys="{keys_str}"' - ) - # avoid cpu consuming object serialization => extra if - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' item="{str(item)}"') + if DEBUGMODE: + LOG.info( + f'Inside dict_item2xml_str: type(obj)="{type(item).__name__}", keys="{keys_str}"' + ) + # avoid cpu consuming object serialization => extra if + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' item="{str(item)}"') if attr_type: attr["type"] = get_xml_type(item) @@ -292,22 +297,24 @@ def convert_dict( ) -> str: """Converts a dict into an XML string.""" keys_str = ", ".join(str(key) for key in obj) - LOG.info( - f'Inside convert_dict(): type(obj)="{type(obj).__name__}", keys="{keys_str}"' - ) - # avoid cpu consuming object serialization => extra if - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' obj="{str(obj)}"') + if DEBUGMODE: + LOG.info( + f'Inside convert_dict(): type(obj)="{type(obj).__name__}", keys="{keys_str}"' + ) + # avoid cpu consuming object serialization => extra if + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' obj="{str(obj)}"') output: List[str] = [] addline = output.append for key, val in obj.items(): - LOG.info( - f'Looping inside convert_dict(): key="{str(key)}", type(val)="{type(val).__name__}"' - ) - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' val="{str(val)}"') + if DEBUGMODE: + LOG.info( + f'Looping inside convert_dict(): key="{str(key)}", type(val)="{type(val).__name__}"' + ) + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' val="{str(val)}"') attr = {} if not ids else {"id": f"{get_unique_id(parent)}"} @@ -377,10 +384,11 @@ def convert_list( item_wrap: bool, ) -> str: """Converts a list into an XML string.""" - LOG.info(f'Inside convert_list(): type(items)="{type(items).__name__}"') - # avoid cpu consuming object serialization => extra if - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' items="{str(items)}"') + if DEBUGMODE: + LOG.info(f'Inside convert_list(): type(items)="{type(items).__name__}"') + # avoid cpu consuming object serialization => extra if + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' items="{str(items)}"') output: List[str] = [] addline = output.append @@ -393,12 +401,13 @@ def convert_list( this_id = get_unique_id(parent) for i, item in enumerate(items): - LOG.info( - f'Looping inside convert_list(): index="{str(i)}", type="{type(item).__name__}"' - ) - # avoid cpu consuming object serialization => extra if - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' item="{str(item)}"') + if DEBUGMODE: + LOG.info( + f'Looping inside convert_list(): index="{str(i)}", type="{type(item).__name__}"' + ) + # avoid cpu consuming object serialization => extra if + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' item="{str(item)}"') attr = {} if not ids else {"id": f"{this_id}_{i + 1}"} @@ -474,9 +483,10 @@ def convert_kv( cdata: bool = False, ) -> str: """Converts a number or string into an XML element""" - LOG.info( - f'Inside convert_kv(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' - ) + if DEBUGMODE: + LOG.info( + f'Inside convert_kv(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' + ) key, attr = make_valid_xml_name(key, attr) if attr_type: @@ -489,9 +499,10 @@ def convert_bool( key: str, val: bool, attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False ) -> str: """Converts a boolean into an XML element""" - LOG.info( - f'Inside convert_bool(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' - ) + if DEBUGMODE: + LOG.info( + f'Inside convert_bool(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' + ) key, attr = make_valid_xml_name(key, attr) if attr_type: @@ -504,7 +515,6 @@ def convert_none( key: str, attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False ) -> str: """Converts a null value into an XML element""" - # LOG.info(f'Inside convert_none(): key="{str(key)}" val={type(val)}') key, attr = make_valid_xml_name(key, attr) if attr_type: @@ -554,12 +564,13 @@ def dicttoxml( {'list': {'@attrs': {'a':'b','c':'d'}, '@val': [4, 5, 6]} which results in 456 """ - LOG.info( - f'Inside dicttoxml(): type(obj) is: "{type(obj).__name__}", type(ids") is : {type(ids).__name__}' - ) - # avoid cpu consuming object serialization (problem for large objects) => extra if - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' obj="{str(obj)}"') + if DEBUGMODE: + LOG.info( + f'Inside dicttoxml(): type(obj) is: "{type(obj).__name__}", type(ids") is : {type(ids).__name__}' + ) + # avoid cpu consuming object serialization (problem for large objects) => extra if + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' obj="{str(obj)}"') output = [] namespacestr = "" diff --git a/tests/test_json2xml.py b/tests/test_json2xml.py index 7ab03711..ada0194c 100644 --- a/tests/test_json2xml.py +++ b/tests/test_json2xml.py @@ -98,7 +98,6 @@ def test_item_wrap(self): xmldata = json2xml.Json2xml(data, pretty=False).to_xml() old_dict = xmltodict.parse(xmldata) # item must be present within my_items - print(xmldata) assert "item" in old_dict['all']['my_items'] assert "item" in old_dict['all']['my_str_items'] @@ -109,7 +108,6 @@ def test_no_item_wrap(self): xmldata = json2xml.Json2xml(data, pretty=False, item_wrap=False).to_xml() old_dict = xmltodict.parse(xmldata) # my_item must be present within my_items - print(xmldata) assert "my_item" in old_dict['all']['my_items'] assert "my_str_items" in old_dict['all'] @@ -119,7 +117,6 @@ def test_empty_array(self): ) xmldata = json2xml.Json2xml(data, pretty=False).to_xml() old_dict = xmltodict.parse(xmldata) - print(xmldata) # item empty_list be present within all assert "empty_list" in old_dict['all'] @@ -129,7 +126,6 @@ def test_attrs(self): ) xmldata = json2xml.Json2xml(data, pretty=False).to_xml() old_dict = xmltodict.parse(xmldata) - print(xmldata) # test all attrs assert "str" == old_dict['all']['my_string']['@type'] assert "int" == old_dict['all']['my_int']['@type'] @@ -181,9 +177,7 @@ def test_read_boolean_data_from_json(self): """Test correct return for boolean types.""" data = readfromjson("examples/booleanjson.json") result = json2xml.Json2xml(data).to_xml() - print(result) dict_from_xml = xmltodict.parse(result) - print(dict_from_xml) assert dict_from_xml["all"]["boolean"]["#text"] != 'True' assert dict_from_xml["all"]["boolean"]["#text"] == 'true' assert dict_from_xml["all"]["boolean_dict_list"]["item"][0]["boolean_dict"]["boolean"]["#text"] == 'true' @@ -195,9 +189,7 @@ def test_read_boolean_data_from_json2(self): """Test correct return for boolean types.""" data = readfromjson("examples/booleanjson2.json") result = json2xml.Json2xml(data).to_xml() - print(result) dict_from_xml = xmltodict.parse(result) - print(dict_from_xml) assert dict_from_xml["all"]["boolean_list"]["item"][0]["#text"] != 'True' assert dict_from_xml["all"]["boolean_list"]["item"][0]["#text"] == 'true' assert dict_from_xml["all"]["boolean_list"]["item"][1]["#text"] == 'false' @@ -212,7 +204,6 @@ def test_dict2xml_with_namespaces(self): data = {'ns1:node1': 'data in namespace 1', 'ns2:node2': 'data in namespace 2'} namespaces = {'ns1': 'http://www.google.de/ns1', 'ns2': 'http://www.google.de/ns2'} result = dicttoxml.dicttoxml(data, attr_type=False, xml_namespaces=namespaces) - print(result) assert b'' \ b'' \ b'data in namespace 1' \ @@ -222,7 +213,6 @@ def test_dict2xml_with_namespaces(self): def test_dict2xml_with_flat(self): data = {'flat_list@flat': [1, 2, 3], 'non_flat_list': [4, 5, 6]} result = dicttoxml.dicttoxml(data, attr_type=False) - print(result) assert b'' \ b'123' \ b'456' \ @@ -232,7 +222,6 @@ def test_dict2xml_with_val_and_custom_attr(self): # in order to use @attr in non-dict objects, we need to lift into a dict and combine with @val as key data = {'list1': [1, 2, 3], 'list2': {'@attrs': {'myattr1': 'myval1', 'myattr2': 'myval2'}, '@val': [4, 5, 6]}} result = dicttoxml.dicttoxml(data, attr_type=False) - print(result) assert b'' \ b'123' \ b'456' \ From 5e9daa5defefdf2d9e7d5e6dc9340f2f3648ec65 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Fri, 6 May 2022 21:33:22 +0530 Subject: [PATCH 2/5] fix: add default value for flag and sort imports --- json2xml/dicttoxml.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json2xml/dicttoxml.py b/json2xml/dicttoxml.py index 1a5f2560..fa939e3a 100755 --- a/json2xml/dicttoxml.py +++ b/json2xml/dicttoxml.py @@ -11,17 +11,17 @@ Items with a `None` type become empty XML elements. This module works with Python 3.7+ """ -import os import datetime import logging import numbers +import os from collections.abc import Callable, Sequence from random import randint from typing import Any, Dict, List, Optional, Tuple, Union from defusedxml.minidom import parseString -DEBUGMODE = os.getenv("DEBUGMODE") +DEBUGMODE = os.getenv("DEBUGMODE", False) LOG = logging.getLogger("dicttoxml") From a52b816519c3bef696257e4eb82bd35837414e16 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Fri, 6 May 2022 21:39:08 +0530 Subject: [PATCH 3/5] fix: make logging lines out of coverage scope --- json2xml/dicttoxml.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/json2xml/dicttoxml.py b/json2xml/dicttoxml.py index fa939e3a..cfb0307e 100755 --- a/json2xml/dicttoxml.py +++ b/json2xml/dicttoxml.py @@ -101,7 +101,7 @@ def make_attrstring(attr: dict[str, Any]) -> str: def key_is_valid_xml(key: str) -> bool: """Checks that a key is a valid XML name""" - if DEBUGMODE: + if DEBUGMODE: # pragma: no cover LOG.info(f'Inside key_is_valid_xml(). Testing "{str(key)}"') test_xml = f'<{key}>foo' try: @@ -113,7 +113,7 @@ def key_is_valid_xml(key: str) -> bool: def make_valid_xml_name(key: str, attr: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: """Tests an XML name and fixes it if invalid""" - if DEBUGMODE: + if DEBUGMODE: # pragma: no cover LOG.info( f'Inside make_valid_xml_name(). Testing key "{str(key)}" with attr "{str(attr)}"' ) @@ -231,7 +231,7 @@ def dict2xml_str( parse dict2xml """ keys_str = ", ".join(str(key) for key in item) - if DEBUGMODE: + if DEBUGMODE: # pragma: no cover LOG.info( f'Inside dict_item2xml_str: type(obj)="{type(item).__name__}", keys="{keys_str}"' ) @@ -297,7 +297,7 @@ def convert_dict( ) -> str: """Converts a dict into an XML string.""" keys_str = ", ".join(str(key) for key in obj) - if DEBUGMODE: + if DEBUGMODE: # pragma: no cover LOG.info( f'Inside convert_dict(): type(obj)="{type(obj).__name__}", keys="{keys_str}"' ) @@ -384,7 +384,7 @@ def convert_list( item_wrap: bool, ) -> str: """Converts a list into an XML string.""" - if DEBUGMODE: + if DEBUGMODE: # pragma: no cover LOG.info(f'Inside convert_list(): type(items)="{type(items).__name__}"') # avoid cpu consuming object serialization => extra if if LOG.getEffectiveLevel() <= logging.DEBUG: @@ -483,7 +483,7 @@ def convert_kv( cdata: bool = False, ) -> str: """Converts a number or string into an XML element""" - if DEBUGMODE: + if DEBUGMODE: # pragma: no cover LOG.info( f'Inside convert_kv(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' ) @@ -499,7 +499,7 @@ def convert_bool( key: str, val: bool, attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False ) -> str: """Converts a boolean into an XML element""" - if DEBUGMODE: + if DEBUGMODE: # pragma: no cover LOG.info( f'Inside convert_bool(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' ) @@ -564,7 +564,7 @@ def dicttoxml( {'list': {'@attrs': {'a':'b','c':'d'}, '@val': [4, 5, 6]} which results in 456 """ - if DEBUGMODE: + if DEBUGMODE: # pragma: no cover LOG.info( f'Inside dicttoxml(): type(obj) is: "{type(obj).__name__}", type(ids") is : {type(ids).__name__}' ) From 4552af5d43d7c8a8cb88808b34f391d3b1ebab7c Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Fri, 6 May 2022 21:41:13 +0530 Subject: [PATCH 4/5] fix: remove log line out of coverage scope --- json2xml/dicttoxml.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json2xml/dicttoxml.py b/json2xml/dicttoxml.py index cfb0307e..9f6c3266 100755 --- a/json2xml/dicttoxml.py +++ b/json2xml/dicttoxml.py @@ -21,8 +21,8 @@ from defusedxml.minidom import parseString -DEBUGMODE = os.getenv("DEBUGMODE", False) -LOG = logging.getLogger("dicttoxml") +DEBUGMODE = os.getenv("DEBUGMODE", False) # pragma: no cover +LOG = logging.getLogger("dicttoxml") # pragma: no cover ids: List[str] = [] # initialize list of unique ids From 45a1c3e649e622c8657064641e775905c1a2c7d0 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Fri, 6 May 2022 21:44:44 +0530 Subject: [PATCH 5/5] add # pragma: no cover for logging lines --- json2xml/dicttoxml.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/json2xml/dicttoxml.py b/json2xml/dicttoxml.py index 9f6c3266..41f751da 100755 --- a/json2xml/dicttoxml.py +++ b/json2xml/dicttoxml.py @@ -165,7 +165,7 @@ def convert( ) -> str: """Routes the elements of an object to the right function to convert them based on their data type""" - if DEBUGMODE: + if DEBUGMODE: # pragma: no cover LOG.info(f'Inside convert(). type(obj)="{type(obj).__name__}"') # avoid cpu consuming object serialization => extra if if LOG.getEffectiveLevel() <= logging.DEBUG: @@ -309,7 +309,7 @@ def convert_dict( addline = output.append for key, val in obj.items(): - if DEBUGMODE: + if DEBUGMODE: # pragma: no cover LOG.info( f'Looping inside convert_dict(): key="{str(key)}", type(val)="{type(val).__name__}"' ) @@ -401,7 +401,7 @@ def convert_list( this_id = get_unique_id(parent) for i, item in enumerate(items): - if DEBUGMODE: + if DEBUGMODE: # pragma: no cover LOG.info( f'Looping inside convert_list(): index="{str(i)}", type="{type(item).__name__}"' )