예제 #1
0
def check_subversion_version():
    """Check that Subversion is compatible.

    """
    # Installed ?
    from subvertpy import ra, __version__ as subvertpy_version
    ra_version = ra.version()
    if (ra_version[1] >= 5 and getattr(ra, 'SVN_REVISION', None)
            and 27729 <= ra.SVN_REVISION < 31470):
        raise DependencyNotPresent(
            "subvertpy",
            'bzr-svn: Installed Subversion has buggy svn.ra.get_log() '
            'implementation, please install newer.')

    from breezy.trace import mutter
    versions = ["Subversion %d.%d.%d (%s)" % ra_version]
    if getattr(ra, "api_version",
               None) is not None and ra.api_version() != ra_version:
        versions.append("Subversion API %d.%d.%d (%s)" % ra.api_version())
    versions.append("subvertpy %d.%d.%d" % subvertpy_version)
    mutter("bzr-svn: using " + ", ".join(versions))

    if subvertpy_version < subvertpy_minimum_version:
        raise DependencyNotPresent(
            "subvertpy",
            "bzr-svn: at least subvertpy %d.%d.%d is required, %d.%d.%d is installed."
            % (subvertpy_minimum_version + subvertpy_version))
예제 #2
0
    def expand(self, begin, todo):
        """Expand

        :param begin: List of path elements currently opened.
        :param todo: List of path elements to still evaluate (including wildcards)
        """
        mutter('expand branches: %r, %r', begin, todo)
        path = u"/".join(begin)
        if (self.project is not None and not self.project.startswith(path)
                and not path.startswith(self.project)):
            return []
        # If all elements have already been handled, just check the path exists
        if len(todo) == 0:
            revnum = self.get_latest_change(path)
            if revnum is not None:
                return [(path, None, int(revnum))]
            else:
                return []
        # Not a wildcard? Just expand next bits
        if todo[0] != u"*":
            return self.expand(begin + [todo[0]], todo[1:])
        children = self.get_children(path)
        if children is None:
            return []
        ret = []
        with ui.ui_factory.nested_progress_bar() as pb:
            for idx, (c, has_props, revnum) in enumerate(children):
                pb.update("browsing branches", idx, len(children))
                if len(todo) == 1:
                    # Last path element, so return directly
                    ret.append((u"/".join(begin + [c]), has_props, revnum))
                else:
                    ret += self.expand(begin + [c], todo[1:])
        return ret
예제 #3
0
파일: tree.py 프로젝트: jelmer/breezy-svn
 def __init__(self, workingtree):
     mutter("opening basistree for %r", workingtree)
     self.workingtree = workingtree
     self._bzr_inventory = None
     self._repository = workingtree.branch.repository
     self.id_map = self.workingtree.basis_idmap
     self.mapping = self.workingtree.branch.mapping
     self._real_tree = None
예제 #4
0
    def test_fetch_odd(self):
        repos_url = self.make_repository('d')

        dc = self.get_commit_editor(repos_url)
        trunk = dc.add_dir("trunk")
        trunk.add_file("trunk/hosts").modify()
        dc.close()

        dc = self.get_commit_editor(repos_url)
        trunk = dc.open_dir("trunk")
        trunk.open_file("trunk/hosts").modify()
        dc.close()

        dc = self.get_commit_editor(repos_url)
        dc.open_file("trunk/hosts").modify()
        dc.close()

        dc = self.get_commit_editor(repos_url)
        dc.add_dir("branches")
        dc.close()

        dc = self.get_commit_editor(repos_url)
        branches = dc.open_dir("branches")
        branches.add_dir("branches/foobranch", "trunk")
        dc.close()

        dc = self.get_commit_editor(repos_url)
        branches = dc.open_dir("branches")
        foobranch = branches.open_dir("branches/foobranch")
        foobranch.open_file("branches/foobranch/hosts").modify()
        dc.close()

        os.mkdir("new")

        url = repos_url + "/branches/foobranch"
        mutter('open %r' % url)
        olddir = ControlDir.open(url)

        newdir = olddir.sprout("new")

        newbranch = newdir.open_branch()
        oldbranch = olddir.open_branch()

        uuid = olddir.find_repository().uuid
        tree = newbranch.repository.revision_tree(
            oldbranch.generate_revision_id(6))
        transaction = newbranch.repository.get_transaction()
        with newbranch.repository.lock_read():
            texts = newbranch.repository.texts
            host_fileid = tree.path2id("hosts")
            mapping = oldbranch.repository.get_mapping()
            self.assertVersionsPresentEquals(texts, host_fileid, [
                mapping.revision_id_foreign_to_bzr((uuid, u"trunk", 1)),
                mapping.revision_id_foreign_to_bzr((uuid, u"trunk", 2)),
                mapping.revision_id_foreign_to_bzr((uuid, u"trunk", 3)),
                oldbranch.generate_revision_id(6)
            ])
예제 #5
0
    def copy_content(self, revision_id=None, basis=None):
        """See InterRepository.copy_content. Partial implementation of that.

        To date the basis parameter is not supported.
        """
        with self.lock_write():
            if basis is not None:
                trace.mutter('Ignoring basis argument %r', basis)
            self.target.fetch(self.source, revision_id=revision_id)
예제 #6
0
 def _invoke(self, command):
     trace.mutter('Will msgmerge: %s' % (command,))
     # We use only absolute paths so we don't care about the cwd
     proc = subprocess.Popen(cmdline.split(command),
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             stdin=subprocess.PIPE)
     out, err = proc.communicate()
     return proc.returncode, out, err
예제 #7
0
파일: scheme.py 프로젝트: jelmer/breezy-svn
def repository_guess_scheme(repository, last_revnum, branch_path=None):
    with ui.ui_factory.nested_progress_bar() as pb:
        (guessed_scheme, scheme) = guess_scheme_from_history(
            repository._log.iter_changes(
                None, last_revnum, max(0, last_revnum - GUESS_SAMPLE_SIZE)),
            last_revnum, branch_path)
    mutter("Guessed branching scheme: %r, guess scheme to use: %r" %
           (guessed_scheme, scheme))
    return (guessed_scheme, scheme)
예제 #8
0
파일: win32utils.py 프로젝트: tpow/breezy
def set_file_attr_hidden(path):
    """Set file attributes to hidden if possible"""
    if has_win32file:
        SetFileAttributes = win32file.SetFileAttributesW
        try:
            SetFileAttributes(path, win32file.FILE_ATTRIBUTE_HIDDEN)
        except pywintypes.error as e:
            from . import trace
            trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
예제 #9
0
파일: auth.py 프로젝트: jelmer/breezy-svn
 def _get_realm(self, credentials):
     if credentials.get('port') is None:
         import socket
         try:
             credentials['port'] = socket.getservbyname(credentials['scheme'])
         except socket.error:
             mutter("Unable to look up default port for %(scheme)s" %
                    credentials)
             return None
     return "<%(scheme)s://%(host)s:%(port)s> %(realm)s" % credentials
예제 #10
0
파일: auth.py 프로젝트: jelmer/breezy-svn
    def get_svn_username(self, realm, may_save):
        """Look up a Subversion user name in the Bazaar authentication cache.

        :param realm: Authentication realm (optional)
        :param may_save: Whether or not the username should be saved.
        """
        mutter("Obtaining username for SVN connection")
        username = self.get_user(self.scheme, host=self.host, path=self.path,
                                 realm=realm, ask=True)
        return (username.encode('utf-8'), False)
예제 #11
0
 def replay_range(self,
                  start_revision,
                  end_revision,
                  low_water_mark,
                  cbs,
                  send_deltas=True):
     mutter("svn replay-range %d -> %d (low water mark: %d)" %
            (start_revision, end_revision, low_water_mark))
     return self.actual.replay_range(start_revision, end_revision,
                                     low_water_mark, cbs, send_deltas)
예제 #12
0
 def test_commit_remove(self):
     wt = self.newdir.open_workingtree()
     self.build_tree({'dc/foob': "data"})
     wt.add("foob")
     wt.commit(message="data")
     wt.remove(["foob"])
     wt.commit(message="doe")
     self.olddir.open_branch().pull(self.newdir.open_branch())
     paths = self.client_log(self.repos_url, 3, 0)[3][0]
     mutter('paths %r' % paths)
     self.assertEquals('D', paths["/foob"][0])
예제 #13
0
def parse_merge_property(line):
    """Parse a bzr:merge property value.

    :param line: Line to parse
    :return: List of revisions merged
    """
    if ' ' in line:
        mutter('invalid revision id %r in merged property, skipping', line)
        return ()

    return tuple(filter(lambda x: x != "", line.split("\t")))
예제 #14
0
def get_roundtrip_ancestor_revids(fileprops):
    for propname, propvalue in fileprops.items():
        if not propname.startswith(SVN_PROP_BZR_REVISION_ID):
            continue
        mapping_name = propname[len(SVN_PROP_BZR_REVISION_ID):]
        for line in propvalue.splitlines():
            try:
                (revno, revid) = parse_revid_property(line)
                yield (revid, revno, mapping_name)
            except errors.InvalidPropertyValue as ie:
                mutter(str(ie))
예제 #15
0
파일: tree.py 프로젝트: jelmer/breezy-svn
 def _map_property(self, name, value):
     if name == "svn:eol-style":
         if value in eol_style:
             return ("eol", eol_style[value])
         mutter("Unknown svn:eol-style setting '%r'", value)
         return None
     elif name == "svn:keywords":
         return ("svn-keywords", value)
     else:
         # Unknown or boring setting
         return None
예제 #16
0
    def helper(self, param=''):
        self._prepare_tree()
        # change dir
        # revert to default revision for file in subdir does work
        os.chdir('dir')
        mutter('cd dir\n')

        self.assertEqual('1\n', self.run_bzr('revno')[0])
        self.run_bzr('revert %s file' % param)
        with open('file', 'rb') as f:
            self.assertEqual(b'spam', f.read())
예제 #17
0
파일: win32utils.py 프로젝트: jelmer/breezy
def set_file_attr_hidden(path):
    """Set file attributes to hidden if possible"""
    from ctypes.wintypes import BOOL, DWORD, LPCWSTR
    _kernel32 = ctypes.windll.kernel32
    # <https://docs.microsoft.com/windows/desktop/api/fileapi/nf-fileapi-setfileattributesw>
    _SetFileAttributesW = ctypes.WINFUNCTYPE(BOOL, LPCWSTR, DWORD)(
        ("SetFileAttributesW", _kernel32))
    FILE_ATTRIBUTE_HIDDEN = 2
    if not SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN):
        e = ctypes.WinError()
        from . import trace
        trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
예제 #18
0
def _run_command(command,
                 basedir,
                 msg,
                 error_msg,
                 not_installed_msg=None,
                 env=None,
                 success_exit_codes=None,
                 indata=None):
    """ Run a command in a subprocess.

    :param command: list with command and parameters
    :param msg: message to display to the user
    :param error_msg: message to display if something fails.
    :param not_installed_msg: the message to display if the command
        isn't available.
    :param env: Optional environment to use rather than os.environ.
    :param success_exit_codes:
        Exit codes to consider succesfull, defaults to [0].
    :param indata: Data to write to standard input
    """
    def subprocess_setup():
        signal.signal(signal.SIGPIPE, signal.SIG_DFL)

    trace.note(msg)
    # Hide output if -q is in use.
    quiet = trace.is_quiet()
    if quiet:
        kwargs = {"stderr": subprocess.STDOUT, "stdout": subprocess.PIPE}
    else:
        kwargs = {}
    if env is not None:
        kwargs["env"] = env
    trace.mutter("running: %r", command)
    try:
        proc = subprocess.Popen(command,
                                cwd=basedir,
                                stdin=subprocess.PIPE,
                                preexec_fn=subprocess_setup,
                                **kwargs)
    except OSError as e:
        if e.errno != errno.ENOENT:
            raise
        if not_installed_msg is None:
            raise
        raise MissingDependency(msg=not_installed_msg)
    output = proc.communicate(indata)
    if success_exit_codes is None:
        success_exit_codes = [0]
    if proc.returncode not in success_exit_codes:
        if quiet:
            raise errors.BzrCommandError("%s: %s" % (error_msg, output))
        else:
            raise errors.BzrCommandError(error_msg)
예제 #19
0
def set_file_attr_hidden(path):
    """Set file attributes to hidden if possible"""
    from ctypes.wintypes import BOOL, DWORD, LPWSTR
    # <https://docs.microsoft.com/windows/desktop/api/fileapi/nf-fileapi-setfileattributesw>
    SetFileAttributes = ctypes.windll.kernel32.SetFileAttributesW
    SetFileAttributes.argtypes = LPWSTR, DWORD
    SetFileAttributes.restype = BOOL
    FILE_ATTRIBUTE_HIDDEN = 2
    if not SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN):
        e = ctypes.WinError()
        from . import trace
        trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
예제 #20
0
 def copy_content_into(self, revision_id=None):
     if revision_id is None:
         revision_id = self.source.last_revision()
     with self.source.lock_read():
         with self.target.lock_write():
             self._push(revision_id, overwrite=True, push_metadata=True)
         try:
             parent = self.source.get_parent()
         except InaccessibleParent as e:
             trace.mutter('parent was not accessible to copy: %s', e)
         else:
             if parent:
                 self.target.set_parent(parent)
예제 #21
0
 def change_prop(self, name, value):
     self.tree.file_properties[self.file_id][name] = value
     if name in (properties.PROP_ENTRY_COMMITTED_DATE,
                 properties.PROP_ENTRY_LAST_AUTHOR,
                 properties.PROP_ENTRY_LOCK_TOKEN,
                 properties.PROP_ENTRY_COMMITTED_REV,
                 properties.PROP_ENTRY_UUID, properties.PROP_IGNORE,
                 properties.PROP_EXECUTABLE):
         pass
     elif name.startswith(properties.PROP_WC_PREFIX):
         pass
     elif name.startswith(properties.PROP_PREFIX):
         mutter('unsupported dir property %r', name)
예제 #22
0
    def test_subdir_commit(self):
        """Test committing a subdirectory, and committing a directory."""
        tree = self.make_branch_and_tree('.')
        b = tree.branch
        self.build_tree(['a/', 'b/'])

        def set_contents(contents):
            self.build_tree_contents([
                ('a/one', contents),
                ('b/two', contents),
                ('top', contents),
            ])

        set_contents(b'old contents')
        tree.smart_add(['.'])
        tree.commit('first revision')
        set_contents(b'new contents')

        mutter('start selective subdir commit')
        self.run_bzr(['commit', 'a', '-m', 'commit a only'])

        new = b.repository.revision_tree(b.get_rev_id(2))
        new.lock_read()

        def get_text_by_path(tree, path):
            return tree.get_file_text(path)

        self.assertEqual(get_text_by_path(new, 'b/two'), b'old contents')
        self.assertEqual(get_text_by_path(new, 'top'), b'old contents')
        self.assertEqual(get_text_by_path(new, 'a/one'), b'new contents')
        new.unlock()

        # commit from here should do nothing
        self.run_bzr(
            ['commit', '.', '-m', 'commit subdir only', '--unchanged'],
            working_dir='a')
        v3 = b.repository.revision_tree(b.get_rev_id(3))
        v3.lock_read()
        self.assertEqual(get_text_by_path(v3, 'b/two'), b'old contents')
        self.assertEqual(get_text_by_path(v3, 'top'), b'old contents')
        self.assertEqual(get_text_by_path(v3, 'a/one'), b'new contents')
        v3.unlock()

        # commit in subdirectory commits whole tree
        self.run_bzr(['commit', '-m', 'commit whole tree from subdir'],
                     working_dir='a')
        v4 = b.repository.revision_tree(b.get_rev_id(4))
        v4.lock_read()
        self.assertEqual(get_text_by_path(v4, 'b/two'), b'new contents')
        self.assertEqual(get_text_by_path(v4, 'top'), b'new contents')
        v4.unlock()
예제 #23
0
def guess_scheme_from_history(changed_paths, last_revnum,
                              relpath=None):
    """Try to determine the best fitting branching scheme.

    :param changed_paths: Iterator over (branch_path, changes, revnum, revprops)
        as returned from LogWalker.iter_changes().
    :param last_revnum: Number of entries in changed_paths.
    :param relpath: Branch path that should be accepted by the branching
                    scheme as a branch.
    :return: Tuple with branching scheme that best matches history and
             branching scheme instance that best matches but also considers
             relpath a valid branch path.
    """
    potentials = {}
    scheme_cache = {}
    with ui.ui_factory.nested_progress_bar() as pb:
        for (revpaths, revnum, revprops) in changed_paths:
            assert isinstance(revpaths, dict)
            pb.update("analyzing repository layout", last_revnum-revnum,
                      last_revnum)
            if revpaths == {}:
                continue
            for path in find_commit_paths([revpaths]):
                scheme = guess_scheme_from_path(path)
                if str(scheme) not in potentials:
                    potentials[str(scheme)] = 0
                potentials[str(scheme)] += 1
                scheme_cache[str(scheme)] = scheme

    entries = list(potentials.items())
    entries.sort(key=operator.itemgetter(1))

    mutter('potential branching schemes: %r' % entries)

    if len(entries) > 0:
        best_match = scheme_cache[entries[0][0]]
    else:
        best_match = None

    if relpath is None:
        if best_match is None:
            return (None, NoBranchingScheme())
        return (best_match, best_match)

    for (schemename, _) in entries:
        scheme = scheme_cache[schemename]
        if scheme.is_branch(relpath):
            return (best_match, scheme)

    return (best_match, guess_scheme_from_branch_path(relpath))
예제 #24
0
 def do_diff(self,
             revision_to_update,
             diff_target,
             versus_url,
             diff_editor,
             recurse=True,
             ignore_ancestry=False,
             text_deltas=False,
             depth=None):
     mutter("svn diff -r%d %s -> %s" %
            (revision_to_update, diff_target, versus_url))
     return self.actual.do_diff(revision_to_update, diff_target, versus_url,
                                diff_editor, recurse, ignore_ancestry,
                                text_deltas)
예제 #25
0
    def get_rhs_parents_fileprops(self, fileprops):
        bzr_merges = fileprops.get(SVN_PROP_BZR_ANCESTRY + self.name, None)
        if bzr_merges is not None:
            try:
                new_lines = find_new_lines(*bzr_merges)
            except ValueError as e:
                mutter(str(e))
                return ()
            if len(new_lines) != 1:
                mutter("unexpected number of lines in bzr merge property: %r",
                       new_lines)
                return ()
            return parse_merge_property(new_lines[0])

        return ()
예제 #26
0
    def push_ancestors(self, layout, project, parent_revids, lossy=False,
                       exclude=None):
        """Push the ancestors of a revision.

        :param layout: Subversion layout
        :param project: Project name
        :param parent_revids: The revision ids of the basic ancestors to push
        """
        present_rhs_parents = self.target.has_revisions(parent_revids[1:])
        unique_ancestors = set()
        missing_rhs_parents = set(parent_revids[1:]) - present_rhs_parents
        graph = self.get_graph()
        for parent_revid in missing_rhs_parents:
            # Push merged revisions
            ancestors = graph.find_unique_ancestors(parent_revid,
                [parent_revids[0]])
            unique_ancestors.update(ancestors)
        for x in self.get_graph().iter_topo_order(unique_ancestors):
            if self._target_has_revision(x, project):
                continue
            rev = self.source.get_revision(x)
            rhs_branch_path = determine_branch_path(rev, layout, project)
            mutter("pushing ancestor %r to %s", x, rhs_branch_path)

            if rev.parent_ids:
                parent_revid = rev.parent_ids[0]
            else:
                parent_revid = NULL_REVISION

            base_foreign_revid, base_mapping = self._get_foreign_revision_info(
                parent_revid)
            if base_foreign_revid is None:
                target_project = None
            else:
                (_, target_project, _, _) = layout.parse(base_foreign_revid[1])
            bp = determine_branch_path(rev, layout, target_project, exclude)
            target_config = self._get_branch_config(bp)
            push_merged = (layout.push_merged_revisions(target_project) and
                target_config.get('push_merged_revisions'))
            root_action = self._get_root_action(bp, rev.parent_ids,
                overwrite=True,
                append_revisions_only=(target_config.get('append_revisions_only') or False),
                create_prefix=True)
            self.push_revision_inclusive(bp, target_config, rev,
                push_metadata=not lossy, push_merged=push_merged,
                layout=layout, project=target_project,
                root_action=root_action,
                base_foreign_info=(base_foreign_revid, base_mapping))
예제 #27
0
 def test_commit_rename_remove_parent(self):
     wt = self.newdir.open_workingtree()
     self.build_tree({'dc/adir/foob': "data"})
     wt.add("adir")
     wt.add("adir/foob")
     wt.commit(message="data")
     wt.rename_one("adir/foob", "bar")
     wt.remove(["adir"])
     wt.commit(message="doe")
     self.olddir.open_branch().pull(self.newdir.open_branch())
     paths = self.client_log(self.repos_url, 3, 0)[3][0]
     mutter('paths %r' % paths)
     self.assertEquals('D', paths["/adir"][0])
     self.assertEquals('A', paths["/bar"][0])
     self.assertEquals('/adir/foob', paths["/bar"][1])
     self.assertEquals(2, paths["/bar"][2])
예제 #28
0
    def test_log_transport(self):
        base_transport = self.get_transport('')
        logging_transport = transport.get_transport('log+' +
                                                    base_transport.base)

        # operations such as mkdir are logged
        mutter('where are you?')
        logging_transport.mkdir('subdir')
        log = self.get_log()
        # GZ 2017-05-24: Used to expect abspath logged, logger needs fixing.
        self.assertContainsRe(log, r'mkdir subdir')
        self.assertContainsRe(log, '  --> None')
        # they have the expected effect
        self.assertTrue(logging_transport.has('subdir'))
        # and they operate on the underlying transport
        self.assertTrue(base_transport.has('subdir'))
예제 #29
0
 def test_commit_rename_file_from_directory(self):
     wt = self.newdir.open_workingtree()
     self.build_tree({'dc/adir/foo': "data"})
     wt.add("adir")
     wt.add("adir/foo")
     wt.commit(message="data")
     wt.rename_one("adir/foo", "bar")
     self.assertTrue(wt.has_filename("bar"))
     self.assertFalse(wt.has_filename("adir/foo"))
     wt.commit(message="doe")
     self.olddir.open_branch().pull(self.newdir.open_branch())
     paths = self.client_log(self.repos_url, 3, 0)[3][0]
     mutter('paths %r' % paths)
     self.assertEquals('D', paths["/adir/foo"][0])
     self.assertEquals('A', paths["/bar"][0])
     self.assertEquals('/adir/foo', paths["/bar"][1])
     self.assertEquals(2, paths["/bar"][2])
예제 #30
0
 def set_tag(self, tag_name, tag_target):
     """Set a new tag in a Subversion repository."""
     path = self.branch.layout.get_tag_path(tag_name, self.branch.project)
     parent = urlutils.dirname(path)
     try:
         (from_uuid, from_bp,
          from_revnum), mapping = self.repository.lookup_bzr_revision_id(
              tag_target, project=self.branch.project)
     except bzr_errors.NoSuchRevision:
         mutter("not setting tag %s; unknown revision %s", tag_name,
                tag_target)
         if GhostTagsNotSupported is not None:
             raise GhostTagsNotSupported(self.branch._format)
         return
     self._ensure_tag_parent_exists(parent)
     try:
         current_from_foreign_revid = self._lookup_tag_revmeta(
             path).metarev.get_foreign_revid()
         deletefirst = True
     except KeyError:
         current_from_foreign_revid = None
         deletefirst = False
     if current_from_foreign_revid == (from_uuid, from_bp, from_revnum):
         # Already present
         return
     mutter("setting tag %s from %r (deletefirst: %r)", path,
            (from_uuid, from_bp, from_revnum), deletefirst)
     conn = self.repository.svn_transport.get_connection(parent)
     try:
         with svn_errors.convert_svn_error(
                 conn.get_commit_editor)(self._revprops(
                     "Add tag %s" % tag_name.encode("utf-8"),
                     {tag_name.encode("utf-8"): tag_target})) as ci:
             root = ci.open_root()
             if deletefirst:
                 root.delete_entry(urlutils.basename(path))
             tag_dir = root.add_directory(
                 urlutils.basename(path),
                 urlutils.join(self.repository.base, from_bp), from_revnum)
             tag_dir.close()
             root.close()
         # FIXME: This shouldn't have to remove the entire cache, just update it
         self.repository._clear_cached_state()
     finally:
         self.repository.svn_transport.add_connection(conn)