From 73b58937eeba238e73fb75a6a713106d979bda0b Mon Sep 17 00:00:00 2001 From: TAHRI Ahmed R Date: Mon, 16 Mar 2020 23:33:32 +0100 Subject: [PATCH 1/8] :sparkle: Add samesite and domain hint for Header class --- kiss_headers/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kiss_headers/utils.py b/kiss_headers/utils.py index 2bfccd8..fac21c5 100644 --- a/kiss_headers/utils.py +++ b/kiss_headers/utils.py @@ -14,6 +14,8 @@ class Header(object): timeout: Union['Header', str] max: Union['Header', str] path: Union['Header', str] + samesite: Union['Header', str] + domain: Union['Header', str] def __init__(self, head: str, content: str): From 1eba6f9ba4e5f14c4f8cd666f1d80a66a2ef0c0c Mon Sep 17 00:00:00 2001 From: TAHRI Ahmed R Date: Mon, 16 Mar 2020 23:34:25 +0100 Subject: [PATCH 2/8] :bug: Should not allow key/attribute to have space in it --- kiss_headers/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kiss_headers/utils.py b/kiss_headers/utils.py index fac21c5..ac7ecf5 100644 --- a/kiss_headers/utils.py +++ b/kiss_headers/utils.py @@ -33,7 +33,7 @@ def __init__(self, head: str, content: str): key, value = tuple(member.split('=', maxsplit=1)) # avoid confusing base64 look alike single value for (key, value) - if value.count('=') == len(value) or len(value) == 0: + if value.count('=') == len(value) or len(value) == 0 or ' ' in key: self._not_valued_attrs.append(member) continue From 505377c38e86a04fac296e6ad4781eb55dd2f894 Mon Sep 17 00:00:00 2001 From: TAHRI Ahmed R Date: Mon, 16 Mar 2020 23:35:42 +0100 Subject: [PATCH 3/8] :art: Minors revisions for type hint _getitem_ _getattr_ can be integer index.. because of type IN/OUT --- kiss_headers/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kiss_headers/utils.py b/kiss_headers/utils.py index ac7ecf5..52d6e8d 100644 --- a/kiss_headers/utils.py +++ b/kiss_headers/utils.py @@ -122,7 +122,7 @@ def get(self, attr: str) -> Optional[str]: return None return self._valued_attrs[attr] - def __getitem__(self, item: str) -> Union[str, List[str]]: + def __getitem__(self, item: Union[str, int]) -> Union[str, List[str]]: """ This method will allow you to retrieve attribute value using the bracket syntax, list-like. """ @@ -142,7 +142,7 @@ def __getitem__(self, item: str) -> Union[str, List[str]]: return value - def __getattr__(self, item) -> str: + def __getattr__(self, item: str) -> str: """ All the magic happen here, this method should be invoked when trying to call (not declared) properties. For instance, calling self.charset should end up here and be replaced by self['charset']. @@ -268,7 +268,7 @@ def __str__(self): def __repr__(self) -> str: return '\n'.join([header.__repr__() for header in self]) - def __getitem__(self, item: str) -> Union[Header, List[Header]]: + def __getitem__(self, item: Union[str, int]) -> Union[Header, List[Header]]: item = Header.normalize_name(item) if item not in self: From 5a2ef3e4fe069bcf4008556c37ab1a7e491a530d Mon Sep 17 00:00:00 2001 From: TAHRI Ahmed R Date: Mon, 16 Mar 2020 23:36:19 +0100 Subject: [PATCH 4/8] :bug: Parse headers even if first n line(s) are not headers --- kiss_headers/utils.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kiss_headers/utils.py b/kiss_headers/utils.py index 52d6e8d..8a6d106 100644 --- a/kiss_headers/utils.py +++ b/kiss_headers/utils.py @@ -330,4 +330,14 @@ def parse_it(raw_headers: Union[bytes, str, Dict[str, str], IOBase]) -> Headers: revised_headers.append((head, revised_content)) + # Sometime raw content does not begin with headers. If that is the case, search for the next line. + if len(revised_headers) == 0 and len(raw_headers) > 0 and (isinstance(raw_headers, bytes) or isinstance(raw_headers, str)): + next_iter = raw_headers.split( + b'\n' if isinstance(raw_headers, bytes) else '\n', + maxsplit=1 + ) + + if len(next_iter) >= 2: + return parse_it(next_iter[-1]) + return Headers([Header(head, content) for head, content in revised_headers]) From c12d20558ca72dc150e94f052b4e7409942f2db8 Mon Sep 17 00:00:00 2001 From: TAHRI Ahmed R Date: Mon, 16 Mar 2020 23:36:33 +0100 Subject: [PATCH 5/8] :pencil: Fix typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1710e7c..c682bcf 100644 --- a/README.md +++ b/README.md @@ -64,10 +64,10 @@ str(headers.content_type) # output : text/html; charset=ISO-8859-1 'application/json' in headers.content_type # output: False 'text/html' in headers.content_type # output: True -str(headers.content_type.charset) # output : utf-8 +str(headers.content_type.charset) # output : ISO-8859-1 ``` -Do not forget that headers are not 1 TO 1. One header can be repeated multiple time and attribute can have multiple within the same header. +Do not forget that headers are not 1 TO 1. One header can be repeated multiple time and attribute can have multiple value within the same header. ```python from kiss_headers import parse_it From bfc167e86b0bd4524831ec43481943833010b365 Mon Sep 17 00:00:00 2001 From: TAHRI Ahmed R Date: Mon, 16 Mar 2020 23:49:10 +0100 Subject: [PATCH 6/8] :sparkle: Support natively responses from Requests --- kiss_headers/utils.py | 10 +++++++++- setup.py | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/kiss_headers/utils.py b/kiss_headers/utils.py index 8a6d106..f4eeb8a 100644 --- a/kiss_headers/utils.py +++ b/kiss_headers/utils.py @@ -3,6 +3,7 @@ from typing import List, Optional, Union, Dict, Mapping, Iterator, Tuple, Iterable from email.header import decode_header from cached_property import cached_property +from requests import Response class Header(object): @@ -303,7 +304,7 @@ def __dir__(self) -> Iterable[str]: return super().__dir__() + list(set([header.normalized_name for header in self])) -def parse_it(raw_headers: Union[bytes, str, Dict[str, str], IOBase]) -> Headers: +def parse_it(raw_headers: Union[bytes, str, Dict[str, str], IOBase, Response]) -> Headers: """ Just decode anything that could represent headers. That simple PERIOD. """ @@ -314,6 +315,13 @@ def parse_it(raw_headers: Union[bytes, str, Dict[str, str], IOBase]) -> Headers: headers = BytesHeaderParser().parse(buf, headersonly=True).items() elif isinstance(raw_headers, Mapping): headers = raw_headers.items() + elif isinstance(raw_headers, Response): + headers = list() + for header_name in raw_headers.raw.headers: + for header_content in raw_headers.raw.headers.getlist(header_name): + headers.append( + (header_name, header_content) + ) else: raise TypeError('Cannot parse type {type_} as it is not supported by kiss-header.'.format(type_=type(raw_headers))) diff --git a/setup.py b/setup.py index d88816e..6796df3 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ def get_version(): REQUIRED = [ 'cached_property', + 'requests' ] EXTRAS = {} From c381e396c088e308b2d223f1ee613c86c7319b3d Mon Sep 17 00:00:00 2001 From: TAHRI Ahmed R Date: Mon, 16 Mar 2020 23:49:42 +0100 Subject: [PATCH 7/8] prepare release 1.0.2 --- kiss_headers/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kiss_headers/version.py b/kiss_headers/version.py index 0701c8f..b1aec0b 100644 --- a/kiss_headers/version.py +++ b/kiss_headers/version.py @@ -2,5 +2,5 @@ Expose version """ -__version__ = "1.0.1" +__version__ = "1.0.2" VERSION = __version__.split('.') From 596496f906ae91e506f630e05d82d78dbffff193 Mon Sep 17 00:00:00 2001 From: TAHRI Ahmed R Date: Mon, 16 Mar 2020 23:55:46 +0100 Subject: [PATCH 8/8] :pencil: Adjust usage to recent changes --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c682bcf..708c33b 100644 --- a/README.md +++ b/README.md @@ -48,14 +48,14 @@ pip install kiss-headers ### 🍰 Usage -`parse_it()` method take `bytes`, `str`, `fp` or `dict` and give you back a `Headers` object. +`parse_it()` method take `bytes`, `str`, `fp`, `dict` or even `requests.Response` itself and give you back a `Headers` object. ```python from requests import get from kiss_headers import parse_it response = get('https://www.google.fr') -headers = parse_it(response.headers) +headers = parse_it(response) 'Content-Type' in headers # output: True 'Content_type' in headers # output: True @@ -65,6 +65,10 @@ str(headers.content_type) # output : text/html; charset=ISO-8859-1 'text/html' in headers.content_type # output: True str(headers.content_type.charset) # output : ISO-8859-1 +type(headers.set_cookie) # output: list +'Secure' in headers.set_cookie[0] # output: True +'domain' in headers.set_cookie[0] # output: True +headers.set_cookie[0].domain # output: .google.fr ``` Do not forget that headers are not 1 TO 1. One header can be repeated multiple time and attribute can have multiple value within the same header.