Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
seth-mg committed Jan 12, 2022
2 parents 7f8e227 + f0fee54 commit b191624
Show file tree
Hide file tree
Showing 11 changed files with 223 additions and 108 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,6 @@ target/

settings.json
*.orig

# Sonar
.scannerwork/
35 changes: 35 additions & 0 deletions DEVELOPER.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
## Developer Information

#### Sonar Scanning
* Uncomment the `sonar.branch.name` line in `sonar-project.properties` and adjust the value to match your branch name.
* Install the `coverage` module in to your virtual environment.
```
virtualenv -p python3 ~/venvs/python-binding-development
source ~/venvs/python-binding-development/bin/activate
pip install --upgrade pip
pip install coverage
```
* Generate the coverage data.
```
coverage run --source=rosette -m pytest
```
* Check the results locally
```
coverage report
```
* Generate the XML coverage report
```
coverage xml
```
* Push the results to Sonar
```
sonar_host=https://sonar.basistech.net
sonar_token=<foo> # Generate a token at https://sonar.basistech.net/account/security/
docker run \
--rm \
-e SONAR_HOST_URL="${sonar_host}" \
-e SONAR_LOGIN="${sonar_token}" \
-v "$(pwd):/usr/src" \
sonarsource/sonar-scanner-cli
```

### Testing
To test changes you have made to the binding, you can use a pre-configured Docker environment. This environment will:
- Compile the binding within the container.
Expand Down
6 changes: 3 additions & 3 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@

# General information about the project.
project = ''
copyright = '2019, Basis Technology'
copyright = '2022, Basis Technology'
author = 'Basis Technology'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.14.4'
version = '1.20.0'
# The full version, including alpha/beta/rc tags.
release = '1.14.4'
release = '1.20.0'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
3 changes: 1 addition & 2 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
[pytest]
pep8ignore = E501
norecursedirs =
.tox
target
target
4 changes: 2 additions & 2 deletions rosette/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
Python client for the Rosette API.
Copyright (c) 2014-2019 Basis Technology Corporation.
Copyright (c) 2014-2022 Basis Technology Corporation.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Expand All @@ -12,4 +12,4 @@
limitations under the License.
"""

__version__ = '1.14.4'
__version__ = '1.20.0'
137 changes: 60 additions & 77 deletions rosette/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
Python client for the Rosette API.
Copyright (c) 2014-2019 Basis Technology Corporation.
Copyright (c) 2014-2022 Basis Technology Corporation.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -28,12 +28,16 @@
import requests
import platform

_BINDING_VERSION = '1.14.4'
_APPLICATION_JSON = 'application/json'
_BINDING_LANGUAGE = 'python'
_BINDING_VERSION = '1.20.0'
_CONCURRENCY_HEADER = 'x-rosetteapi-concurrency'
_CUSTOM_HEADER_PREFIX = 'X-RosetteAPI-'
_CUSTOM_HEADER_PATTERN = re.compile('^' + _CUSTOM_HEADER_PREFIX)
_GZIP_BYTEARRAY = bytearray([0x1F, 0x8b, 0x08])

_ISPY3 = sys.version_info[0] == 3


if _ISPY3:
_GZIP_SIGNATURE = _GZIP_BYTEARRAY
else:
Expand All @@ -49,7 +53,6 @@ def __init__(self, js, code):
self.status_code = code

def json(self):
""" return json"""
return self._json


Expand Down Expand Up @@ -112,7 +115,7 @@ def serialize(self, options):
values = {}
for (key, val) in self.__params.items():
if val is None:
pass
continue
else:
values[key] = val

Expand Down Expand Up @@ -242,7 +245,7 @@ def validate(self):
if self[option] is None:
raise RosetteException(
"missingParameter",
"Required Name Translation parameter, " + option + ", not supplied",
"Required Name Translation parameter is missing: " + option,
repr(option))


Expand All @@ -268,7 +271,7 @@ def validate(self):
if self[option] is None:
raise RosetteException(
"missingParameter",
"Required Address Similarity parameter, " + option + ", not supplied",
"Required Address Similarity parameter is missing: " + option,
repr(option))


Expand Down Expand Up @@ -301,7 +304,7 @@ def validate(self):
if self[option] is None:
raise RosetteException(
"missingParameter",
"Required Name Similarity parameter, " + option + ", not supplied",
"Required Name Similarity parameter is missing: " + option,
repr(option))


Expand All @@ -321,7 +324,7 @@ def validate(self):
if self["names"] is None: # required
raise RosetteException(
"missingParameter",
"Required Name De-Duplication parameter, names, not supplied",
"Required Name De-Duplication parameter is missing: names",
repr("names"))


Expand Down Expand Up @@ -372,31 +375,37 @@ def __finish_result(self, response, ename):
raise RosetteException(code, complaint_url +
" : failed to communicate with Rosette", msg)

def info(self):
"""Issues an "info" request to the L{EndpointCaller}'s specific endpoint.
@return: A dictionary telling server version and other
identifying data."""
url = self.service_url + self.api.endpoints["INFO"]
headers = {'Accept': 'application/json', 'X-RosetteAPI-Binding': 'python',
'X-RosetteAPI-Binding-Version': _BINDING_VERSION}
def __set_headers(self):
headers = {'Accept': _APPLICATION_JSON,
_CUSTOM_HEADER_PREFIX + 'Binding': _BINDING_LANGUAGE,
_CUSTOM_HEADER_PREFIX + 'Binding-Version': _BINDING_VERSION}

custom_headers = self.api.get_custom_headers()
pattern = re.compile('^X-RosetteAPI-')
if custom_headers is not None:
for key in custom_headers.keys():
if pattern.match(key) is not None:
if _CUSTOM_HEADER_PATTERN.match(key) is not None:
headers[key] = custom_headers[key]
else:
raise RosetteException("badHeader",
"Custom header name must begin with \"X-RosetteAPI-\"",
"Custom header name must begin with \"" + _CUSTOM_HEADER_PREFIX + "\"",
key)
self.api.clear_custom_headers()

if self.debug:
headers['X-RosetteAPI-Devel'] = 'true'
self.logger.info('info: ' + url)
headers[_CUSTOM_HEADER_PREFIX + 'Devel'] = 'true'

if self.user_key is not None:
headers["X-RosetteAPI-Key"] = self.user_key
headers[_CUSTOM_HEADER_PREFIX + "Key"] = self.user_key

return headers

def info(self):
"""Issues an "info" request to the L{EndpointCaller}'s specific endpoint.
@return: A dictionary telling server version and other
identifying data."""
url = self.service_url + self.api.endpoints["INFO"]
headers = self.__set_headers()
self.logger.info('info: ' + url)
response = self.api.get_http(url, headers=headers)
return self.__finish_result(response, "info")

Expand All @@ -407,26 +416,8 @@ def ping(self):
signalled."""

url = self.service_url + self.api.endpoints['PING']
headers = {'Accept': 'application/json', 'X-RosetteAPI-Binding': 'python',
'X-RosetteAPI-Binding-Version': _BINDING_VERSION}

custom_headers = self.api.get_custom_headers()
pattern = re.compile('^X-RosetteAPI-')
if custom_headers is not None:
for key in custom_headers.keys():
if pattern.match(key) is not None:
headers[key] = custom_headers[key]
else:
raise RosetteException("badHeader",
"Custom header name must begin with \"X-RosetteAPI-\"",
key)
self.api.clear_custom_headers()

if self.debug:
headers['X-RosetteAPI-Devel'] = 'true'
headers = self.__set_headers()
self.logger.info('Ping: ' + url)
if self.user_key is not None:
headers["X-RosetteAPI-Key"] = self.user_key
response = self.api.get_http(url, headers=headers)
return self.__finish_result(response, "ping")

Expand Down Expand Up @@ -454,9 +445,9 @@ def call(self, parameters):

if not isinstance(parameters, _DocumentParamSetBase):
if self.suburl != self.api.endpoints['NAME_SIMILARITY'] \
and self.suburl != self.api.self.api.endpoints['NAME_TRANSLATION'] \
and self.suburl != self.api.self.api.endpoints['NAME_DEDUPLICATION'] \
and self.suburl != self.api.self.api.endpoints['ADDRESS_SIMILARITY']:
and self.suburl != self.api.self.api.endpoints['NAME_TRANSLATION'] \
and self.suburl != self.api.self.api.endpoints['NAME_DEDUPLICATION'] \
and self.suburl != self.api.self.api.endpoints['ADDRESS_SIMILARITY']:
text = parameters
parameters = DocumentParameters()
parameters['content'] = text
Expand All @@ -471,22 +462,7 @@ def call(self, parameters):
params_to_serialize = parameters.serialize(self.api.options)
headers = {}
if self.user_key is not None:
custom_headers = self.api.get_custom_headers()
pattern = re.compile('^X-RosetteAPI-')
if custom_headers is not None:
for key in custom_headers.keys():
if pattern.match(key) is not None:
headers[key] = custom_headers[key]
else:
raise RosetteException("badHeader",
"Custom header name must "
"begin with \"X-RosetteAPI-\"",
key)
self.api.clear_custom_headers()

headers["X-RosetteAPI-Key"] = self.user_key
headers["X-RosetteAPI-Binding"] = "python"
headers["X-RosetteAPI-Binding-Version"] = _BINDING_VERSION
headers = self.__set_headers()

if self.use_multipart:
payload = None
Expand All @@ -496,7 +472,7 @@ def call(self, parameters):
params = dict(
(key,
value) for key,
value in params_to_serialize.items() if key == 'language')
value in params_to_serialize.items() if key == 'language')
files = {
'content': (
os.path.basename(
Expand All @@ -506,7 +482,7 @@ def call(self, parameters):
'request': (
'request_options',
json.dumps(params),
'application/json')}
_APPLICATION_JSON)}
request = requests.Request(
'POST', url, files=files, headers=headers, params=payload)
prepared_request = self.api.session.prepare_request(request)
Expand All @@ -519,11 +495,11 @@ def call(self, parameters):
_my_loads(rdata, response_headers), status)
else:
if self.debug:
headers['X-RosetteAPI-Devel'] = True
headers[_CUSTOM_HEADER_PREFIX + 'Devel'] = True
self.logger.info('operate: ' + url)
headers['Accept'] = "application/json"
headers['Accept'] = _APPLICATION_JSON
headers['Accept-Encoding'] = "gzip"
headers['Content-Type'] = "application/json"
headers['Content-Type'] = _APPLICATION_JSON
response = self.api.post_http(url, params_to_serialize, headers)
return self.__finish_result(response, "operate")

Expand Down Expand Up @@ -613,13 +589,21 @@ def get_user_agent_string(self):
""" Return the User-Agent string """
return self.user_agent_string

def _set_pool_size(self):
def set_pool_size(self, new_pool_size):
"""Sets the connection pool size.
@parameter new_pool_size: pool size to set
"""
self.max_pool_size = new_pool_size
adapter = requests.adapters.HTTPAdapter(
pool_maxsize=self.max_pool_size)
pool_maxsize=new_pool_size)
if 'https:' in self.service_url:
self.session.mount('https://', adapter)
else:
self.session.mount('http://', adapter)
self.session.mount('http://', adapter) # NOSONAR

def __adjust_concurrency(self, dict_headers):
if _CONCURRENCY_HEADER in dict_headers and dict_headers[_CONCURRENCY_HEADER] != self.max_pool_size:
self.set_pool_size(dict_headers[_CONCURRENCY_HEADER])

def _make_request(self, operation, url, data, headers):
"""
Expand Down Expand Up @@ -650,11 +634,8 @@ def _make_request(self, operation, url, data, headers):
status = response.status_code
rdata = response.content
dict_headers = dict(response.headers)
self.__adjust_concurrency(dict_headers)
response_headers = {"responseHeaders": dict_headers}
if 'x-rosetteapi-concurrency' in dict_headers:
if dict_headers['x-rosetteapi-concurrency'] != self.max_pool_size:
self.max_pool_size = dict_headers['x-rosetteapi-concurrency']
self._set_pool_size()

if status == 200:
return rdata, status, response_headers
Expand All @@ -670,9 +651,11 @@ def _make_request(self, operation, url, data, headers):
if not message:
message = rdata
raise RosetteException(code, message, url)

except:
raise
except json.JSONDecodeError as exception:
raise RosetteException(
exception,
"Problem decoding JSON",
rdata)
except requests.exceptions.RequestException as exception:
raise RosetteException(
exception,
Expand Down Expand Up @@ -964,12 +947,12 @@ def name_deduplication(self, parameters):
return EndpointCaller(self, self.endpoints['NAME_DEDUPLICATION']).call(parameters)

def text_embedding(self, parameters):
"""
""" deprecated
Create an L{EndpointCaller} to identify text vectors found in the texts
to which it is applied and call it.
@type parameters: L{DocumentParameters} or L{str}
@return: A python dictionary containing the results of text embedding."""
return EndpointCaller(self, self.endpoints['TEXT_EMBEDDING']).call(parameters)
return self.semantic_vectors(parameters)

def semantic_vectors(self, parameters):
"""
Expand Down
4 changes: 4 additions & 0 deletions sonar-project.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
sonar.projectKey=rosette-api-python-binding
sonar.sources=rosette
sonar.python.coverage.reportPaths=coverage.xml
#sonar.branch.name=RCB-596-pool-size
Loading

0 comments on commit b191624

Please sign in to comment.