starting with a proof of concept. It IS possible to monkey patch socket module
This commit is contained in:
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
*.pyc
|
||||
.coverage
|
||||
docs/_build/
|
||||
httpretty.egg-info/
|
||||
build/
|
||||
dist/
|
||||
.DS_Store
|
||||
*.swp
|
||||
.#*
|
||||
#*
|
||||
22
COPYING
Normal file
22
COPYING
Normal file
@@ -0,0 +1,22 @@
|
||||
Copyright (C) <2011> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
40
Makefile
Normal file
40
Makefile
Normal file
@@ -0,0 +1,40 @@
|
||||
all: check_dependencies unit functional integration doctest
|
||||
|
||||
filename=httpretty-`python -c 'import httpretty;print httpretty.version'`.tar.gz
|
||||
|
||||
#export PYTHONPATH:= ${PWD}
|
||||
export HTTPRETTY_DEPENDENCIES:= nose sure
|
||||
|
||||
check_dependencies:
|
||||
@echo "Checking for dependencies to run tests ..."
|
||||
@for dependency in `echo $$HTTPRETTY_DEPENDENCIES`; do \
|
||||
python -c "import $$dependency" 2>/dev/null || (echo "You must install $$dependency in order to run httpretty's tests" && exit 3) ; \
|
||||
done
|
||||
|
||||
unit: clean
|
||||
@echo "Running unit tests ..."
|
||||
@nosetests -s --verbosity=2 --with-coverage --cover-erase --cover-inclusive tests/unit --cover-package=httpretty
|
||||
|
||||
functional: clean
|
||||
@echo "Running functional tests ..."
|
||||
@nosetests -s --verbosity=2 --with-coverage --cover-erase --cover-inclusive tests/functional --cover-package=httpretty
|
||||
|
||||
integration: clean
|
||||
@echo "Running integration tests ..."
|
||||
@nosetests -s --verbosity=2 tests/integration
|
||||
|
||||
doctest: clean
|
||||
@cd docs && make doctest
|
||||
|
||||
documentation:
|
||||
@cd docs && make html
|
||||
|
||||
clean:
|
||||
@printf "Cleaning up files that are already in .gitignore... "
|
||||
@for pattern in `cat .gitignore`; do rm -rf $$pattern; done
|
||||
@echo "OK!"
|
||||
|
||||
release: clean unit functional integration doctest deploy-documentation
|
||||
@printf "Exporting to $(filename)... "
|
||||
@tar czf $(filename) httpretty setup.py README.md COPYING
|
||||
@echo "DONE!"
|
||||
89
README.md
Normal file
89
README.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# HTTPretty
|
||||
> Version 0.1
|
||||
|
||||
# What
|
||||
|
||||
HTTPretty is a HTTP client mock library for Python 100% inspired on ruby's [FakeWeb](http://fakeweb.rubyforge.org/)
|
||||
|
||||
# Motivation
|
||||
|
||||
When building systems that access external resources such as RESTful
|
||||
webservices, XMLRPC or even simple HTTP requests, we stumble in the
|
||||
problem:
|
||||
|
||||
"I'm gonna need to mock all those requests"
|
||||
|
||||
It brings a lot of hassle, you will need to use a generic mocking
|
||||
tool, mess with scope and so on.
|
||||
|
||||
## The idea behind HTTPretty (how it works)
|
||||
|
||||
HTTPretty [monkey matches](http://en.wikipedia.org/wiki/Monkey_patch)
|
||||
Python's [socket](http://docs.python.org/library/socket.html) core
|
||||
module, reimplementing the HTTP protocol, by mocking requests and
|
||||
responses.
|
||||
|
||||
This is a nice thing, if you consider that mostly python http module
|
||||
will be "mockable".
|
||||
|
||||
# Usage
|
||||
|
||||
from httpretty import HTTPretty
|
||||
HTTPretty.register_uri(HTTPretty.GET, "http://globo.com/",
|
||||
body="The biggest portal in Brazil")
|
||||
|
||||
fd = urllib2.urlopen('http://globo.com')
|
||||
got = fd.read()
|
||||
fd.close()
|
||||
|
||||
print got
|
||||
|
||||
**:: output ::**
|
||||
|
||||
The biggest portal in Brazil
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
you will need **ONLY** if you decide to contribute to HTTPretty which means you're gonna need run our test suite
|
||||
|
||||
* [nose](http://code.google.com/p/python-nose/)
|
||||
> [sudo] pip install nose
|
||||
* [sure](http://github.com/gabrielfalcao/sure/)
|
||||
> [sudo] pip install sure
|
||||
|
||||
# Contributing
|
||||
|
||||
1. fork and clone the project
|
||||
2. install the dependencies above
|
||||
3. run the tests with make:
|
||||
> make unit functional integration
|
||||
4. hack at will
|
||||
5. commit, push etc
|
||||
6. send a pull request
|
||||
|
||||
# License
|
||||
|
||||
<HTTPretty - HTTP client mock for Python>
|
||||
Copyright (C) <2011> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
144
httpretty/__init__.py
Normal file
144
httpretty/__init__.py
Normal file
@@ -0,0 +1,144 @@
|
||||
# #!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
# files (the "Software"), to deal in the Software without
|
||||
# restriction, including without limitation the rights to use,
|
||||
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following
|
||||
# conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
import re
|
||||
import socket
|
||||
from urlparse import urlsplit
|
||||
from StringIO import StringIO
|
||||
|
||||
class fakesock(object):
|
||||
old_socket = socket.socket
|
||||
|
||||
class socket(object):
|
||||
_entry = None
|
||||
def __init__(self, family, type, protocol):
|
||||
self.family = family
|
||||
self.type = type
|
||||
self.protocol = protocol
|
||||
|
||||
def connect(self, address):
|
||||
self._address = (self._host, self._port) = address
|
||||
self._closed = False
|
||||
|
||||
def close(self):
|
||||
self._closed = True
|
||||
|
||||
def makefile(self, mode, bufsize):
|
||||
self._mode = mode
|
||||
self._bufsize = bufsize
|
||||
fd = StringIO()
|
||||
|
||||
if self._entry:
|
||||
fd.write(self._entry.body.strip())
|
||||
fd.seek(0)
|
||||
|
||||
|
||||
return fd
|
||||
|
||||
def sendall(self, data):
|
||||
verb, headers_string = data.split('\n', 1)
|
||||
method, path, version = re.split('\s+', verb.strip(), 3)
|
||||
|
||||
info = URIInfo(hostname=self._host, port=self._port, path=path)
|
||||
|
||||
entry = HTTPretty._entries.get(info, None)
|
||||
if not entry:
|
||||
return
|
||||
|
||||
if entry.method == method:
|
||||
self._entry = entry
|
||||
|
||||
def create_fake_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
|
||||
s = fakesock.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
|
||||
if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT:
|
||||
s.settimeout(timeout)
|
||||
|
||||
s.connect(address)
|
||||
return s
|
||||
|
||||
|
||||
|
||||
class Entry(object):
|
||||
def __init__(self, method, uri, body):
|
||||
self.method = method
|
||||
self.uri = uri
|
||||
self.body = body
|
||||
|
||||
class URIInfo(object):
|
||||
def __init__(self, username='', password='', hostname='', port=80, path='/', query='', fragment=''):
|
||||
self.username = username or ''
|
||||
self.password = password or ''
|
||||
self.hostname = hostname or ''
|
||||
self.port = int(port) != 80 and str(port) or ''
|
||||
self.path = path or ''
|
||||
self.query = query or ''
|
||||
self.fragment = fragment or ''
|
||||
|
||||
def __unicode__(self):
|
||||
attrs = 'username', 'password', 'hostname', 'port', 'path', 'query', 'fragment'
|
||||
return ur'<httpretty.URIInfo(%s)>' % ", ".join(['%s="%s"' % (k, getattr(self, k, '')) for k in attrs])
|
||||
|
||||
def __repr__(self):
|
||||
return unicode(self)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(unicode(self))
|
||||
|
||||
def __eq__(self, other):
|
||||
return unicode(self) == unicode(other)
|
||||
|
||||
@classmethod
|
||||
def from_uri(cls, uri):
|
||||
result = urlsplit(uri)
|
||||
return cls(result.username,
|
||||
result.password,
|
||||
result.hostname,
|
||||
result.port or 80,
|
||||
result.path,
|
||||
result.query,
|
||||
result.fragment)
|
||||
|
||||
|
||||
class HTTPretty(object):
|
||||
u"""The URI registration class"""
|
||||
_entries = {}
|
||||
|
||||
GET = 'GET'
|
||||
PUT = 'PUT'
|
||||
POST = 'POST'
|
||||
DELETE = 'DELETE'
|
||||
HEAD = 'HEAD'
|
||||
|
||||
@classmethod
|
||||
def register_uri(self, method, uri, body):
|
||||
self._entries[URIInfo.from_uri(uri)] = Entry(method, uri, body)
|
||||
|
||||
def __repr__(self):
|
||||
return u'<HTTPretty with %d URI entries>' % len(self._entries)
|
||||
|
||||
socket.socket = fakesock.socket
|
||||
socket.create_connection = create_fake_connection
|
||||
socket.create = fakesock.socket
|
||||
socket.__dict__.update(fakesock.__dict__)
|
||||
49
setup.py
Executable file
49
setup.py
Executable file
@@ -0,0 +1,49 @@
|
||||
# #!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
# files (the "Software"), to deal in the Software without
|
||||
# restriction, including without limitation the rights to use,
|
||||
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following
|
||||
# conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
import os
|
||||
from httpretty import version
|
||||
from setuptools import setup
|
||||
|
||||
def get_packages():
|
||||
# setuptools can't do the job :(
|
||||
packages = []
|
||||
for root, dirnames, filenames in os.walk('httpretty'):
|
||||
if '__init__.py' in filenames:
|
||||
packages.append(".".join(os.path.split(root)).strip("."))
|
||||
|
||||
return packages
|
||||
|
||||
setup(name='httpretty',
|
||||
version=version,
|
||||
description='HTTP client mock for Python',
|
||||
author=u'Gabriel Falcao',
|
||||
author_email='gabriel@nacaolivre.org',
|
||||
url='http://github.com/gabrielfalcao/httpretty',
|
||||
packages=get_packages()
|
||||
)
|
||||
26
tests/functional/__init__.py
Normal file
26
tests/functional/__init__.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# #!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
# files (the "Software"), to deal in the Software without
|
||||
# restriction, including without limitation the rights to use,
|
||||
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following
|
||||
# conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
42
tests/functional/test_urllib2.py
Normal file
42
tests/functional/test_urllib2.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# #!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
# files (the "Software"), to deal in the Software without
|
||||
# restriction, including without limitation the rights to use,
|
||||
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following
|
||||
# conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
import urllib2
|
||||
from sure import that, within, microseconds
|
||||
from httpretty import HTTPretty
|
||||
|
||||
#@within(five=microseconds) # ensure time to timeout actual request
|
||||
def test_httpretty_should_mock_a_simple_get_with_urllib2_read():
|
||||
u"HTTPretty should mock a simple GET with urllib2.read()"
|
||||
|
||||
HTTPretty.register_uri(HTTPretty.GET, "http://globo.com/",
|
||||
body="The biggest portal in Brazil")
|
||||
|
||||
fd = urllib2.urlopen('http://globo.com')
|
||||
got = fd.read()
|
||||
fd.close()
|
||||
|
||||
assert that(got).equals('The biggest portal in Brazil')
|
||||
26
tests/integration/__init__.py
Normal file
26
tests/integration/__init__.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# #!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
# files (the "Software"), to deal in the Software without
|
||||
# restriction, including without limitation the rights to use,
|
||||
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following
|
||||
# conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
26
tests/unit/__init__.py
Normal file
26
tests/unit/__init__.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# #!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# <HTTPretty - HTTP client mock for Python>
|
||||
# Copyright (C) <2011> Gabriel Falcão <gabriel@nacaolivre.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
# files (the "Software"), to deal in the Software without
|
||||
# restriction, including without limitation the rights to use,
|
||||
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following
|
||||
# conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Reference in New Issue
Block a user