Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
csparpa committed Jan 2, 2016
2 parents bf6a4ea + 69cf68f commit 0f7ecb8
Show file tree
Hide file tree
Showing 30 changed files with 303 additions and 130 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ sphinx/_build/*
.project
.pydevproject
.idea/*
.tox/*
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "wiki"]
path = wiki
url = https://github.com/csparpa/pyowm.wiki.git
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ python:
- "3.2"
- "3.3"
install:
- pip install coveralls
- pip install coverage==3.7.1 tox==1.9.2
script:
- python setup.py install
- python setup.py test -s tests.unit
- tox
- coverage run --rcfile=.coveragerc setup.py test -s tests.unit
after_success:
coveralls
notifications:
email:
on_failure: change
sudo: false
6 changes: 6 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ Code
----
* [liato] (https://github.com/liato)
* [Noid] (https://github.com/n0id)
* [dphildebrandt] (https://github.com/dphildebrandt)

Testing
-------
* [Samuel Yap] (https://github.com/samuelyap)

Wiki
----
* [richarddunks] (https://github.com/richarddunks)
* [solumos] (https://github.com/solumos)
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ PyOWM
=====
A Python wrapper around the OpenWeatherMap API

[![PyPI version](https://badge.fury.io/py/pyowm.svg)](https://badge.fury.io/py/pyowm)
[![Build Status](https://travis-ci.org/csparpa/pyowm.png?branch=master)](https://travis-ci.org/csparpa/pyowm)
[![Coverage Status](https://coveralls.io/repos/csparpa/pyowm/badge.png?branch=develop)](https://coveralls.io/r/csparpa/pyowm?branch=develop)
[![Latest Version](https://pypip.in/version/pyowm/badge.svg)](https://pypi.python.org/pypi/pyowm/)
[![Downloads](https://pypip.in/download/pyowm/badge.svg?period=week)](https://pypi.python.org/pypi/pyowm/)
[![Downloads](https://img.shields.io/pypi/dm/pyowm.svg)](https://img.shields.io/pypi/dm/pyowm.svg)

What is it?
------------
Expand All @@ -27,6 +27,9 @@ Usage examples
import pyowm

owm = pyowm.OWM('your-API-key')

# You have a pro subscription? Use:
# owm = pyowm.OWM(API_key='your-API-key', subscription_type='pro')

# Will it be sunny tomorrow at this time in Milan (Italy) ?
forecast = owm.daily_forecast("Milan,it")
Expand Down Expand Up @@ -91,7 +94,13 @@ The library API documentation is available on [Read the Docs](https://pyowm.read

Test
----
Unit testing is as simple as `python setup.py test -s tests.unit`
Unit testing is as simple as `python setup.py test -s tests.unit`. This shall
be done for each different Python interpreter supported by PyOWM.

A more straighforward way to run tests is using [Tox](http://tox.readthedocs.org).
From the project root folder, just launch:

`tox`

PyOWM is continuously built with [Travis-CI](https://travis-ci.org/csparpa/pyowm) and code coverage is checked
with [Coveralls.io](https://coveralls.io/r/csparpa/pyowm)
Expand All @@ -101,6 +110,8 @@ Development
Contributors (code enhancement, issue/bug reporting) are __welcome!__. See the
[notes on development](https://github.com/csparpa/pyowm/wiki/Notes-on-development) to get started.

Since version 2.2 PyOWM adopts [Semantic Versioning](http://semver.org/).

If you liked PyOWM, [consider giving me a tip](https://gratipay.com/csparpa)!

References
Expand Down
4 changes: 4 additions & 0 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
coverage==3.7.1
Sphinx==1.2.1
tox==1.9.2
virtualenv==12.0.7
17 changes: 13 additions & 4 deletions docs/build.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
PyOWM release checklist
-----------------------
* update setup.py
* consider major, minor and patch version numbers according to SemVer
* update constants.py
* update setup.py
* update city ID files
* update README.md
* update github wiki pages (including changelog)
* run tests locally using setup.py
* update github wiki pages (including changelog) in the /wiki folder
* run tests locally using tox (or setup.py with all Python supported envs)
* generate documentation locally
* merge develop branch into master branch (no feature/hotfix branches left open)
* close milestone on github
* tag release on github
* upload release on pypi
* generate and upload release on pypi


Filling in of main setup.py file
Expand Down Expand Up @@ -43,6 +45,13 @@ The .egg will be installed into the system-dependent Python libraries folder:
/usr/local/lib/pythonX.Y/dist-packages # Ubuntu
/usr/local/lib/pythonX.Y/dist-packages # MacOS 10.5.4

Clone the wiki as a submodule
-----------------------------
Run:

git submodule update --init


Build documentation
-------------------
First install Sphinx:
Expand Down
11 changes: 9 additions & 2 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,15 @@ of the PyOWM library that call the real PyOWM web API via HTTP).
The default unit testing enviroment is Python's _unittest_: simple and clean
enough, so no additional dependencies are needed.

Unit tests can be launched by moving into the library installation folder and
executing:
Unit tests can be easily run using _tox_. Running:

tox

triggers unit tests execution on all Python platfoms that are supported by
PyOQM.

Unit tests can also be launched by moving into the library installation folder
and executing:

python -m unittest discover

Expand Down
12 changes: 12 additions & 0 deletions docs/wiki.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
GitHub project wiki
-------------------

The Wiki is handled as a Git submodule of the project.

The submodule is "mounted" under the '/wiki' folder.

These are the instructions to setup the submodule:

$> cd <PYOWM-root>
$> git submodule add https://github.com/csparpa/pyowm.wiki.git wiki
$> git submodule init
12 changes: 10 additions & 2 deletions pyowm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@


def OWM(API_key=None, version=constants.LATEST_OWM_API_VERSION,
config_module=None, language=None):
config_module=None, language=None, subscription_type=None):
"""
A parametrized factory method returning a global OWM instance that
represents the desired OWM web API version (or the currently supported one
Expand All @@ -36,6 +36,10 @@ def OWM(API_key=None, version=constants.LATEST_OWM_API_VERSION,
It's a two-characters string, eg: "en", "ru", "it". Defaults to:
``None``, which means use the default language.
:type language: str
:param subscription_type: the type of OWM web API subscription to be wrapped.
Can be 'free' (free subscription) or 'pro' (paid subscription),
Defaults to: 'free'
:type subscription_type: str
:returns: an instance of a proper *OWM* subclass
:raises: *ValueError* when unsupported OWM API versions are provided
"""
Expand All @@ -46,6 +50,10 @@ def OWM(API_key=None, version=constants.LATEST_OWM_API_VERSION,
from pyowm.webapi25.owm25 import OWM25
if language is None:
language = cfg_module.language
if subscription_type is None:
subscription_type = cfg_module.API_SUBSCRIPTION_TYPE
if subscription_type not in ['free', 'pro']:
subscription_type = 'free'
return OWM25(cfg_module.parsers, API_key, cfg_module.cache,
language)
language, subscription_type)
raise ValueError("Unsupported OWM web API version")
23 changes: 17 additions & 6 deletions pyowm/commons/owmhttpclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@

import socket
from pyowm.exceptions import api_call_error

from pyowm.webapi25.configuration25 import ROOT_API_URL

class OWMHTTPClient(object):

API_SUBSCRIPTION_SUBDOMAINS = {
'free': 'api',
'pro': 'pro'
}

"""
An HTTP client class, that can leverage a cache mechanism.
Expand All @@ -24,12 +29,17 @@ class OWMHTTPClient(object):
:param cache: an *OWMCache* concrete instance that will be used to
cache OWM web API responses.
:type cache: an *OWMCache* concrete instance
:param subscription_type: the type of OWM web API subscription to be wrapped.
The value is used to pick the proper API subdomain for HTTP calls.
Defaults to: 'free'
:type subscription_type: str
"""

def __init__(self, API_key, cache):
def __init__(self, API_key, cache, subscription_type='free'):
self._API_key = API_key
self._cache = cache
self._API_root_URL = ROOT_API_URL % \
(self.API_SUBSCRIPTION_SUBDOMAINS[subscription_type],)

def call_API(self, API_endpoint_URL, params_dict,
timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
Expand Down Expand Up @@ -61,9 +71,9 @@ def call_API(self, API_endpoint_URL, params_dict,
from urllib2 import urlopen
response = urlopen(url, None, timeout)
except HTTPError as e:
raise api_call_error.APICallError(str(e.reason), e)
raise api_call_error.APICallError(str(e), e)
except URLError as e:
raise api_call_error.APICallError(str(e.reason), e)
raise api_call_error.APICallError(str(e), e)
else:
data = response.read().decode('utf-8')
self._cache.set(url, data)
Expand All @@ -84,10 +94,11 @@ def _build_full_URL(self, API_endpoint_URL, params_dict):
:returns: a full string HTTP request URL
"""
url =self._API_root_URL + API_endpoint_URL
params = params_dict.copy()
if self._API_key is not None:
params['APPID'] = self._API_key
return self._build_query_parameters(API_endpoint_URL, params)
return self._build_query_parameters(url, params)

def _build_query_parameters(self, base_URL, params_dict):
"""
Expand Down
2 changes: 1 addition & 1 deletion pyowm/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
Constants for the PyOWM library
"""

PYOWM_VERSION = '2.2.1'
PYOWM_VERSION = '2.3.0'
LATEST_OWM_API_VERSION = '2.5'
5 changes: 4 additions & 1 deletion pyowm/webapi25/cityidregistry.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ def _lookup_line_by_city_name(self, city_name):

def _get_lines(self, filename):
with resource_stream(__name__, filename) as f:
return f.readlines()
lines = f.readlines()
if type(lines[0]) is bytes:
lines = map(lambda l: l.decode("utf-8"), lines)
return lines

def _match_line(self, city_name, lines):
for line in lines:
Expand Down
23 changes: 13 additions & 10 deletions pyowm/webapi25/configuration25.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@
"""

# OWM web API URLs
ROOT_API_URL = 'http://api.openweathermap.org/data/2.5'
ROOT_API_URL = 'http://%s.openweathermap.org/data/2.5'
ICONS_BASE_URL = 'http://openweathermap.org/img/w'
OBSERVATION_URL = ROOT_API_URL + '/weather'
STATION_URL = ROOT_API_URL + '/station'
FIND_OBSERVATIONS_URL = ROOT_API_URL + '/find'
FIND_STATION_URL = ROOT_API_URL + '/station/find'
BBOX_STATION_URL = ROOT_API_URL + '/box/station'
THREE_HOURS_FORECAST_URL = ROOT_API_URL + '/forecast'
DAILY_FORECAST_URL = ROOT_API_URL + '/forecast/daily'
CITY_WEATHER_HISTORY_URL = ROOT_API_URL + '/history/city'
STATION_WEATHER_HISTORY_URL = ROOT_API_URL + '/history/station'
OBSERVATION_URL = '/weather'
STATION_URL = '/station'
FIND_OBSERVATIONS_URL = '/find'
FIND_STATION_URL = '/station/find'
BBOX_STATION_URL = '/box/station'
THREE_HOURS_FORECAST_URL = '/forecast'
DAILY_FORECAST_URL = '/forecast/daily'
CITY_WEATHER_HISTORY_URL = '/history/city'
STATION_WEATHER_HISTORY_URL = '/history/station'

# Parser objects injection for OWM web API responses parsing
parsers = {
Expand All @@ -46,6 +46,9 @@
# Default language for OWM web API queries text results
language = 'en'

# Default API subscription type ('free' or 'pro')
API_SUBSCRIPTION_TYPE = 'free'

# OWM web API availability test timeout in seconds
API_AVAILABILITY_TIMEOUT = 2

Expand Down
29 changes: 25 additions & 4 deletions pyowm/webapi25/owm25.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,25 @@ class OWM25(owm.OWM):
:param language: the language in which you want text results to be returned.
It's a two-characters string, eg: "en", "ru", "it". Defaults to: "en"
:type language: str
:param subscription_type: the type of OWM web API subscription to be wrapped.
Can be 'free' (free subscription) or 'pro' (paid subscription),
Defaults to: 'free'
:type subscription_type: str
:returns: an *OWM25* instance
"""
def __init__(self, parsers, API_key=None, cache=nullcache.NullCache(),
language="en"):
language="en", subscription_type='free'):
self._parsers = parsers
if API_key is not None:
OWM25._assert_is_string("API_key", API_key)
self._API_key = API_key
self._httpclient = owmhttpclient.OWMHTTPClient(API_key, cache)
self._httpclient = owmhttpclient.OWMHTTPClient(API_key, cache,
subscription_type)
self._language = language
if API_key is None and subscription_type == 'pro':
raise AssertionError('You must provide an API Key for paid subscriptions')
self._subscription_type = subscription_type

@staticmethod
def _assert_is_string(name, value):
Expand Down Expand Up @@ -111,6 +119,16 @@ def set_language(self, language):
"""
self._language = language

def get_subscription_type(self):
"""
Returns the OWM API subscription type
:returns: the subscription type
"""
return self._subscription_type


def city_id_registry(self):
"""
Gives the *CityIDRegistry* singleton instance that can be used to lookup
Expand Down Expand Up @@ -823,8 +841,11 @@ def _retrieve_station_history(self, station_ID, limit, interval):

def __repr__(self):
return "<%s.%s - API key=%s, OWM web API version=%s, " \
"PyOWM version=%s, language=%s>" % (__name__,
"subscription type=%s, PyOWM version=%s, language=%s>" % \
(__name__,
self.__class__.__name__,
self._API_key, self.get_API_version(),
self._API_key,
self.get_API_version(),
self._subscription_type,
self.get_version(),
self._language)
9 changes: 6 additions & 3 deletions pyowm/webapi25/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,13 +463,17 @@ def weather_from_dictionary(d):
elif 'last' in d:
if 'wind' in d['last']:
wind = d['last']['wind'].copy()
elif 'speed' in d:
wind = dict(speed=d['speed'])
else:
wind = dict()
# -- humidity
if 'humidity' in d:
humidity = d['humidity']
elif 'main' in d and 'humidity' in d['main']:
humidity = d['main']['humidity']
elif 'last' in d and 'main' in d['last'] and 'humidity' in d['last']['main']:
humidity = d['last']['main']['humidity']
else:
humidity = 0
# -- snow
Expand Down Expand Up @@ -524,9 +528,8 @@ def weather_from_dictionary(d):
temperature = dict()
# -- weather status info
if 'weather' in d:
# Sometimes provided with a leading upper case!
status = d['weather'][0]['main'].lower()
detailed_status = d['weather'][0]['description'].lower()
status = d['weather'][0]['main']
detailed_status = d['weather'][0]['description']
weather_code = d['weather'][0]['id']
weather_icon_name = d['weather'][0]['icon']
else:
Expand Down
Loading

0 comments on commit 0f7ecb8

Please sign in to comment.