Skip to content

Commit 1419c3e

Browse files
authored
Merge pull request #2 from joshowen/2.0
Updated for modern Django versions
2 parents 3303438 + 97726fa commit 1419c3e

File tree

15 files changed

+170
-68
lines changed

15 files changed

+170
-68
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
.idea/
3+
4+
*.pyc
5+
6+
.tox/

.travis.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
language: python
2+
python:
3+
- "3.5"
4+
- "3.4"
5+
- "2.7"
6+
sudo: false
7+
env:
8+
- TOX_ENV=django19
9+
- TOX_ENV=django18
10+
- TOX_ENV=django110
11+
- TOX_ENV=djangomaster
12+
matrix:
13+
fast_finish: true
14+
allow_failures:
15+
- env: TOX_ENV=djangomaster
16+
install:
17+
- pip install tox
18+
script:
19+
- tox -e $TOX_ENV

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2.0 - TBD
2+
=====================
3+
- Support Django-1.8+ and Python3
14

25
1.1 - December 2012
36
=====================

README.rst

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
django-url-robots
33
=========================
44

5-
``Django`` ``robots.txt`` generator. Based on using decorated ``django.conf.urls.defaults.url``.
5+
``Django`` ``robots.txt`` generator. Based on using decorated ``django.conf.urls.url``.
66
It gets ``urlpatterns`` and replaces ambiguous parts by ``*``.
77

88
Installation & Usage
@@ -24,28 +24,28 @@ The recommended way to install django-url-robots is with `pip <http://pypi.pytho
2424

2525
3. Add url_robots view to your root URLconf::
2626

27-
urlpatterns += patterns('',
28-
url(r'^robots\.txt$', 'url_robots.views.robots_txt'),
29-
)
27+
urlpatterns += [
28+
url(r'^robots\.txt$', url_robots.views.robots_txt),
29+
]
3030

31-
4. Describe rules by boolean keyword argument ``robots_allow`` using for it ``url_robots.utils.url`` instead ``django.conf.urls.defaults.url``::
31+
4. Describe rules by boolean keyword argument ``robots_allow`` using for it ``url_robots.utils.url`` instead ``django.conf.urls.url``::
3232

3333
from url_robots.utils import url
3434
35-
urlpatterns += patterns('',
36-
url('^profile/private$', 'view', robots_allow=False),
37-
)
35+
urlpatterns += [
36+
url('^profile/private$', views.some_view, robots_allow=False),
37+
]
3838
39-
``django-url-robots`` tested with ``Django-1.3``. Encodes unicode characters by percent-encoding.
39+
``django-url-robots`` tested with ``Django-1.8+``. Encodes unicode characters by percent-encoding.
4040

4141
Settings
4242
====================
4343

4444
In this moment there are only one option to define template of ``robots.txt`` file::
4545

46-
urlpatterns += patterns('',
47-
url(r'^robots\.txt$', 'url_robots.views.robots_txt', {'template': 'my_awesome_robots_template.txt'}),
48-
)
46+
urlpatterns += [
47+
url(r'^robots\.txt$', url_robots.views.robots_txt, {'template': 'my_awesome_robots_template.txt'}),
48+
]
4949

5050
Example
5151
===================
@@ -57,23 +57,22 @@ robots_template.txt::
5757

5858
urls.py::
5959

60-
from django.conf.urls.defaults import patterns, include
60+
from django.conf.urls import include
6161

62-
urlpatterns = patterns('',
62+
urlpatterns = [
6363
url(r'^profile', include('url_robots.tests.urls_profile')),
64-
)
64+
]
6565

6666
urls_profile.py::
6767

68-
from django.conf.urls.defaults import patterns
6968
from url_robots.utils import url
7069

71-
urlpatterns = patterns('',
72-
url(r'^s$', 'view', name='profiles', robots_allow=True),
73-
url(r'^/(?P<nick>\w+)$', 'view'),
74-
url(r'^/(?P<nick>\w+)/private', 'view', name='profile_private', robots_allow=False),
75-
url(r'^/(?P<nick>\w+)/public', 'view', name='profile_public', robots_allow=True),
76-
)
70+
urlpatterns = [
71+
url(r'^s$', views.some_view, name='profiles', robots_allow=True),
72+
url(r'^/(?P<nick>\w+)$', views.some_view),
73+
url(r'^/(?P<nick>\w+)/private', views.some_view, name='profile_private', robots_allow=False),
74+
url(r'^/(?P<nick>\w+)/public', views.some_view, name='profile_public', robots_allow=True),
75+
]
7776

7877
Resulting robots.txt::
7978

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Django>=1.6

runtests.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env python
2+
3+
import sys
4+
5+
6+
DEFAULT_SETTINGS = dict(
7+
INSTALLED_APPS=[
8+
'django.contrib.auth',
9+
'django.contrib.contenttypes',
10+
'django.contrib.sites',
11+
'url_robots',
12+
],
13+
DATABASES={
14+
'default': {
15+
'ENGINE': 'django.db.backends.sqlite3'
16+
}
17+
},
18+
ROOT_URLCONF='tests.test_utils.urls',
19+
SITE_ID=1,
20+
MIDDLEWARE_CLASSES=[
21+
'django.middleware.http.ConditionalGetMiddleware',
22+
'django.contrib.sessions.middleware.SessionMiddleware',
23+
'django.contrib.auth.middleware.AuthenticationMiddleware',
24+
'django.contrib.messages.middleware.MessageMiddleware',
25+
'django.middleware.csrf.CsrfViewMiddleware',
26+
'django.middleware.locale.LocaleMiddleware',
27+
'django.middleware.common.CommonMiddleware',
28+
],
29+
)
30+
31+
32+
def runtests():
33+
import django
34+
from django.conf import settings
35+
36+
# Compatibility with Django 1.7's stricter initialization
37+
if not settings.configured:
38+
settings.configure(**DEFAULT_SETTINGS)
39+
if hasattr(django, 'setup'):
40+
django.setup()
41+
42+
from django.test.runner import DiscoverRunner
43+
test_args = ['url_robots.tests']
44+
failures = DiscoverRunner(
45+
verbosity=1, interactive=True, failfast=False
46+
).run_tests(test_args)
47+
sys.exit(failures)
48+
49+
50+
if __name__ == '__main__':
51+
runtests()

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
setup(
66
name='django-url-robots',
7-
version='1.1',
7+
version='2.0',
88
description='Django robots.txt generator',
99
long_description=long_description,
1010
url='http://github.com/dimka665/django-url-robots',
@@ -24,4 +24,4 @@
2424
'Environment :: Web Environment',
2525
'Framework :: Django',
2626
],
27-
)
27+
)

tox.ini

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[tox]
2+
envlist =
3+
django18,
4+
django19,
5+
django110,
6+
django{master}
7+
8+
[testenv]
9+
deps =
10+
django18: Django==1.8.16
11+
django19: Django==1.9.11
12+
django110: Django==1.10.3
13+
djangomaster: https://github.com/django/django/archive/master.tar.gz
14+
15+
commands = {env:COMMAND:python} runtests.py

url_robots/templates/robots.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
User-agent: *
2+
{{ rules|safe }}
3+
Crawl-delay: 1

url_robots/templates/robots_template.txt

Lines changed: 0 additions & 4 deletions
This file was deleted.

url_robots/tests/tests.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# coding=utf-8
22

3-
# todo On Django 1.4 change testcase to django.test.SimpleTestCase
4-
from django.utils import unittest
3+
import unittest
54

65
from url_robots.utils import clean_pattern, join_patterns, create_rules
76

@@ -46,8 +45,8 @@ def test_begin_of_string(self):
4645
self.clean_equal(r'^ab', 'ab')
4746

4847
def test_end_of_string(self):
49-
self.assertEqual(clean_pattern(ur'ab$'), u'/ab$')
50-
self.assertEqual(clean_pattern(ur'ab.$'), u'/ab*')
48+
self.assertEqual(clean_pattern(r'ab$'), u'/ab$')
49+
self.assertEqual(clean_pattern(r'ab.$'), u'/ab*')
5150

5251
def test_greedy_repetitions(self):
5352
self.clean_equal(r'a.*b', 'a*b')
@@ -98,7 +97,7 @@ def test_urlquote(self):
9897
self.clean_equal(r'star-\*', 'star-%2A')
9998
self.clean_equal(r'dollar-\$', 'dollar-%24')
10099
# some russian language
101-
self.clean_equal(ur'path/в никуда/', ur'path/%D0%B2%20%D0%BD%D0%B8%D0%BA%D1%83%D0%B4%D0%B0/')
100+
self.clean_equal(u'path/в никуда/', r'path/%D0%B2%20%D0%BD%D0%B8%D0%BA%D1%83%D0%B4%D0%B0/')
102101

103102

104103
class JoinPatternTestCase(unittest.TestCase):
@@ -137,9 +136,9 @@ def test_1(self):
137136

138137
class CreateRulesTestCase(unittest.TestCase):
139138
def setUp(self):
140-
self.expected_rules = '''Allow: /profiles$ # /profiles$ name=profiles
141-
Disallow: /profile/*/private* # /profile/*/private* name=profile_private
142-
Allow: /profile/*/public* # /profile/*/public* name=profile_public'''
139+
self.expected_rules = '''Allow: /profiles$
140+
Disallow: /profile/*/private*
141+
Allow: /profile/*/public*'''
143142

144143
def test_create_rules_for_profiles(self):
145144
rules = create_rules('url_robots.tests.urls')

url_robots/tests/urls.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#coding=utf-8
22

3-
from django.conf.urls.defaults import patterns, include
3+
from django.conf.urls import include
44

55
from url_robots.utils import url
66

77

8-
urlpatterns = patterns('',
8+
urlpatterns = [
99
url(r'^profile', include('url_robots.tests.urls_profile')),
10-
)
10+
]

url_robots/tests/urls_profile.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
#coding=utf-8
22

3-
from django.conf.urls.defaults import patterns
43

54
from url_robots.utils import url
65

7-
urlpatterns = patterns('',
8-
url(r'^s$', 'view', name='profiles', robots_allow=True),
9-
url(r'^/(?P<nick>\w+)$', 'view'),
10-
url(r'^/(?P<nick>\w+)/private', 'view', name='profile_private', robots_allow=False),
11-
url(r'^/(?P<nick>\w+)/public', 'view', name='profile_public', robots_allow=True),
12-
)
6+
urlpatterns = [
7+
url(r'^s$', lambda x: (), name='profiles', robots_allow=True),
8+
url(r'^/(?P<nick>\w+)$', lambda x: ()),
9+
url(r'^/(?P<nick>\w+)/private', lambda x: (), name='profile_private', robots_allow=False),
10+
url(r'^/(?P<nick>\w+)/public', lambda x: (), name='profile_public', robots_allow=True),
11+
]

url_robots/utils.py

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,24 @@
22
import sre_parse
33
from sre_constants import LITERAL, AT, AT_BEGINNING, AT_END
44

5-
from urllib import quote, unquote
6-
7-
from django.conf.urls.defaults import url
5+
try:
6+
# Python 3
7+
from urllib.parse import quote, unquote
8+
unichr = chr
9+
except ImportError:
10+
# Python 2
11+
from urllib import quote, unquote
12+
13+
from django.conf.urls import url
814
from django.core.urlresolvers import get_urlconf, get_resolver, RegexURLResolver
915

10-
# decorator for django.conf.urls.defaults.url
16+
1117
def robots_decorator(url_function):
12-
def url_extended(regex, view, kwargs=None, name=None, prefix='',
13-
robots_allow=None):
14-
resolver_or_pattern = url_function(regex, view, kwargs=kwargs, name=name, prefix=prefix)
18+
"""
19+
Decorator for django.conf.urls.url
20+
"""
21+
def url_extended(regex, view, kwargs=None, name=None, robots_allow=None):
22+
resolver_or_pattern = url_function(regex, view, kwargs=kwargs, name=name)
1523

1624
resolver_or_pattern.robots_allow = robots_allow
1725
return resolver_or_pattern
@@ -22,15 +30,22 @@ def url_extended(regex, view, kwargs=None, name=None, prefix='',
2230

2331

2432
def create_rules(urlconf=None):
33+
"""
34+
Creates rules from conf
35+
"""
2536
if urlconf is None:
2637
urlconf = get_urlconf()
38+
2739
root_resolver = get_resolver(urlconf)
2840
rule_list = create_rule_list(root_resolver, '')
41+
2942
return u'\n'.join(rule_list)
3043

3144

3245
def create_rule_list(parent_resolver, abs_pattern):
33-
46+
"""
47+
Creates usable rule list
48+
"""
3449
rule_list = []
3550

3651
for resolver in parent_resolver.url_patterns:
@@ -49,15 +64,6 @@ def create_rule_list(parent_resolver, abs_pattern):
4964
if rule:
5065
path = clean_pattern(pattern)
5166
rule += path
52-
length = 120
53-
rule = rule.ljust(length)
54-
55-
rule += ' # ' + unquote(path).decode('utf8')
56-
length += 40
57-
rule = rule.ljust(length)
58-
59-
rule += ' name=' + (getattr(resolver, 'name', '') or '')
60-
6167
rule_list.append(rule)
6268

6369
if isinstance(resolver, RegexURLResolver):
@@ -67,6 +73,9 @@ def create_rule_list(parent_resolver, abs_pattern):
6773

6874

6975
def join_patterns(pattern1, pattern2):
76+
"""
77+
Joins URL patterns
78+
"""
7079
if pattern1.endswith('$'):
7180
return pattern1
7281

@@ -79,10 +88,13 @@ def join_patterns(pattern1, pattern2):
7988
return pattern1
8089

8190

82-
# pattern => token
83-
# '2' => ('literal', 50)
84-
# '2|3' => ('in', [('literal', 50), ('literal', 51)])
8591
def clean_pattern(pattern):
92+
"""
93+
Cleans URL patterns
94+
* pattern => token
95+
* '2' => ('literal', 50)
96+
* '2|3' => ('in', [('literal', 50), ('literal', 51)])
97+
"""
8698
star = '*'
8799
parsed = sre_parse.parse(pattern)
88100
literals = []
@@ -91,7 +103,6 @@ def clean_pattern(pattern):
91103
if token[0] == LITERAL:
92104
character = quote(unichr(token[1]).encode('utf8'))
93105
literals.append(character)
94-
95106
elif token[0] == AT:
96107
pass
97108

0 commit comments

Comments
 (0)