Permalink
Browse files

Enforce extensions ordering in load/unload

Ordering can't be easily supported on Python 2.4, as it is not compatible
with simplejson versions supporting object_pairs_hook.
  • Loading branch information...
1 parent bdb333f commit 906da1870832f2f48da67fc3f880674eccc5694e @dvarrazzo committed Nov 26, 2011
View
@@ -8,16 +8,12 @@
from urllib import urlencode
-from pgxnclient.utils import json
+from pgxnclient.utils import load_json
from pgxnclient.errors import NetworkError, NotFound, ResourceNotFound
from pgxnclient.network import get_file
from pgxnclient.utils.uri import expand_template
-def load_json(f):
- data = f.read().decode('utf-8')
- return json.loads(data)
-
class Api(object):
def __init__(self, mirror):
self.mirror = mirror
@@ -15,7 +15,7 @@
import logging
from subprocess import Popen, PIPE
-from pgxnclient.utils import json
+from pgxnclient.utils import load_json
from pgxnclient.utils import argparse
from pgxnclient import __version__
@@ -372,7 +372,7 @@ def get_meta(self, spec):
raise PgxnClientException(
_("file 'META.json' not found in '%s'") % dir)
- return json.load(open(fn))
+ return load_json(open(fn))
elif spec.is_file():
# Get the metadata from a zip file
@@ -8,6 +8,7 @@
import os
import re
+import sys
import shutil
import difflib
import logging
@@ -340,8 +341,10 @@ def run(self):
spec = self.get_spec()
dist = self.get_meta(spec)
- # TODO: probably unordered before Python 2.7 or something
if 'provides' in dist:
+ if len(dist['provides']) > 1 and sys.version_info < (2, 5):
+ logger.warn(_("can't guarantee extensions load order "
+ "with Python < 2.5"))
for name, data in dist['provides'].items():
sql = data.get('file')
self.load_ext(name, sql)
@@ -417,8 +420,10 @@ def run(self):
spec = self.get_spec()
dist = self.get_meta(spec)
- # TODO: ensure ordering
if 'provides' in dist:
+ if len(dist['provides']) > 1 and sys.version_info < (2, 5):
+ logger.warn(_("can't guarantee extensions load order "
+ "with Python < 2.5"))
provs = dist['provides'].items()
provs.reverse()
for name, data in provs:
@@ -528,7 +528,6 @@ def test_load_local_zip(self, mock_get, mock_popen, mock_unpack,
self.assertEquals(pop.communicate.call_args[0][0],
'CREATE EXTENSION foobar;')
-
@patch('pgxnclient.commands.install.Load.is_extension')
@patch('pgxnclient.commands.install.Load.get_pg_version')
@patch('pgxnclient.commands.Popen')
@@ -557,6 +556,74 @@ def test_load_local_dir(self, mock_get, mock_popen,
self.assertEquals(pop.communicate.call_args[0][0],
'CREATE EXTENSION foobar;')
+ @patch('pgxnclient.commands.install.Load.is_extension')
+ @patch('pgxnclient.commands.install.Load.get_pg_version')
+ @patch('pgxnclient.commands.Popen')
+ def test_load_extensions_order(self, mock_popen, mock_pgver, mock_isext):
+ pop = mock_popen.return_value
+ pop.returncode = 0
+ mock_pgver.return_value = (9,1,0)
+ mock_isext.return_value = True
+
+ tdir = tempfile.mkdtemp()
+ try:
+ from pgxnclient.utils.zip import unpack
+ dir = unpack(get_test_filename('foobar-0.42.1.zip'), tdir)
+ shutil.copyfile(
+ get_test_filename('META-manyext.json'),
+ os.path.join(dir, 'META.json'))
+
+ from pgxnclient.cli import main
+ main(['load', '--yes', dir])
+
+ finally:
+ shutil.rmtree(tdir)
+
+ self.assertEquals(mock_popen.call_count, 4)
+ self.assert_('psql' in mock_popen.call_args[0][0][0])
+ self.assertEquals(pop.communicate.call_args_list[0][0][0],
+ 'CREATE EXTENSION foo;')
+ self.assertEquals(pop.communicate.call_args_list[1][0][0],
+ 'CREATE EXTENSION bar;')
+ self.assertEquals(pop.communicate.call_args_list[2][0][0],
+ 'CREATE EXTENSION baz;')
+ self.assertEquals(pop.communicate.call_args_list[3][0][0],
+ 'CREATE EXTENSION qux;')
+
+ @patch('pgxnclient.commands.install.Unload.is_extension')
+ @patch('pgxnclient.commands.install.Unload.get_pg_version')
+ @patch('pgxnclient.commands.Popen')
+ def test_unload_extensions_order(self, mock_popen, mock_pgver, mock_isext):
+ pop = mock_popen.return_value
+ pop.returncode = 0
+ mock_pgver.return_value = (9,1,0)
+ mock_isext.return_value = True
+
+ tdir = tempfile.mkdtemp()
+ try:
+ from pgxnclient.utils.zip import unpack
+ dir = unpack(get_test_filename('foobar-0.42.1.zip'), tdir)
+ shutil.copyfile(
+ get_test_filename('META-manyext.json'),
+ os.path.join(dir, 'META.json'))
+
+ from pgxnclient.cli import main
+ main(['unload', '--yes', dir])
+
+ finally:
+ shutil.rmtree(tdir)
+
+ self.assertEquals(mock_popen.call_count, 4)
+ self.assert_('psql' in mock_popen.call_args[0][0][0])
+ self.assertEquals(pop.communicate.call_args_list[0][0][0],
+ 'DROP EXTENSION qux;')
+ self.assertEquals(pop.communicate.call_args_list[1][0][0],
+ 'DROP EXTENSION baz;')
+ self.assertEquals(pop.communicate.call_args_list[2][0][0],
+ 'DROP EXTENSION bar;')
+ self.assertEquals(pop.communicate.call_args_list[3][0][0],
+ 'DROP EXTENSION foo;')
+
class SearchTestCase(unittest.TestCase):
@patch('sys.stdout')
@@ -7,16 +7,39 @@
# This file is part of the PGXN client
+__all__ = ['OrderedDict', 'load_json', 'load_jsons', 'sha1', 'b']
+
+
+import sys
+
+# OrderedDict available from Python 2.7
+if sys.version_info >= (2, 7):
+ from collections import OrderedDict
+else:
+ from pgxnclient.utils.ordereddict import OrderedDict
+
+
# Import the proper JSON library
# dependencies note: simplejson is certified for Python 2.5, and supports
# Python 2.4 up to version 2.0.9. After that the package is in the stdlib
-
-import sys
if sys.version_info >= (2, 6):
import json
else:
import simplejson as json
+def load_json(f):
+ return load_jsons(f.read().decode('utf-8'))
+
+def load_jsons(data):
+ # order required to keep "provides" extensions in order.
+ # Python 2.4 is only compatible with a simplejson version that doesn't
+ # support ordered dict.
+ if sys.version_info >= (2, 5):
+ return json.loads(data, object_pairs_hook=OrderedDict)
+ else:
+ return json.loads(data)
+
+
# Import the sha1 object without warnings
if sys.version_info >= (2, 5):
from hashlib import sha1
Oops, something went wrong.

0 comments on commit 906da18

Please sign in to comment.