You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2022/01/14 14:01:51 UTC

svn commit: r1897034 [7/37] - in /subversion/branches/multi-wc-format: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/ contrib/client-side/svn_load_dirs/ contrib/hook-scripts/ contrib/se...

Modified: subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java Fri Jan 14 14:01:45 2022
@@ -22,7 +22,10 @@
  */
 package org.apache.subversion.javahl;
 
+import static org.junit.Assert.*;
+
 import org.apache.subversion.javahl.callback.*;
+import org.apache.subversion.javahl.remote.*;
 import org.apache.subversion.javahl.types.*;
 
 import java.io.File;
@@ -34,6 +37,7 @@ import java.io.PrintWriter;
 import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.WritableByteChannel;
 import java.text.ParseException;
@@ -2736,7 +2740,7 @@ public class BasicTests extends SVNTests
                 public void singleMessage(Set<ChangePath> changedPaths,
                     long revision, Map<String, byte[]> revprops,
                     boolean hasChildren)
-                { revList.add(new Long(revision)); }
+                { revList.add(Long.valueOf(revision)); }
             });
 
         long[] revisions = new long[revList.size()];
@@ -4093,6 +4097,7 @@ public class BasicTests extends SVNTests
      * @throws Throwable
      * @since 1.5
      */
+    @SuppressWarnings("deprecation")
     public void testBasicBlame() throws Throwable
     {
         OneTest thisTest = new OneTest();
@@ -4122,6 +4127,7 @@ public class BasicTests extends SVNTests
      * Test blame with diff options.
      * @since 1.9
      */
+    @SuppressWarnings("deprecation")
     public void testBlameWithDiffOptions() throws Throwable
     {
         OneTest thisTest = new OneTest();
@@ -4155,6 +4161,49 @@ public class BasicTests extends SVNTests
     }
 
     /**
+     * Test the new 1.12 blame interface on a file with null bytes.
+     * @throws Throwable
+     * @since 1.12
+     */
+    public void testBinaryBlame() throws Throwable
+    {
+        final byte[] lineIn = {0x0, 0x0, 0x0, 0xa};
+        final byte[] lineOut = {0x0, 0x0, 0x0};
+
+        OneTest thisTest = new OneTest();
+        // Modify the file iota, adding null bytes.
+        File iota = new File(thisTest.getWorkingCopy(), "iota");
+        FileOutputStream stream = new FileOutputStream(iota, false);
+        stream.write(lineIn);
+        stream.close();
+        Set<String> srcPaths = new HashSet<String>(1);
+        srcPaths.add(thisTest.getWCPath());
+        try {
+            client.username("rayjandom");
+            client.commit(srcPaths, Depth.infinity, false, false, null, null,
+                          new ConstMsg("NUL bytes written to /iota"), null);
+        } finally {
+            client.username("jrandom");
+        }
+
+        // Test the current interface
+        BlameRangeCallbackImpl rangeCallback = new BlameRangeCallbackImpl();
+        BlameLineCallbackImpl lineCallback = new BlameLineCallbackImpl();
+        client.blame(thisTest.getWCPath() + "/iota", Revision.HEAD,
+                     Revision.getInstance(0), Revision.HEAD,
+                     false, false, null, rangeCallback, lineCallback);
+        assertEquals(0, rangeCallback.startRevnum);
+        assertEquals(2, rangeCallback.endRevnum);
+        assertEquals(1, lineCallback.numberOfLines());
+
+        BlameLineCallbackImpl.BlameLine line = lineCallback.getBlameLine(0);
+        assertNotNull(line);
+        assertEquals(2, line.getRevision());
+        assertEquals("rayjandom", line.getAuthor());
+        assertArrayEquals(lineOut, line.getLine());
+    }
+
+    /**
      * Test commit of arbitrary revprops.
      * @throws Throwable
      * @since 1.5
@@ -4370,6 +4419,320 @@ public class BasicTests extends SVNTests
         assertEquals("fake", new String(revprop));
     }
 
+    public static int FLAG_ECHO          = 0x00000001;
+    public static int FLAG_THROW_IN_OPEN = 0x00000002;
+
+    public enum Actions
+    {
+        READ_CLIENT,    // Read a request from SVN client
+        EMUL_SERVER,    // Emulate server response
+        WAIT_TUNNEL,    // Wait for tunnel to be closed
+    };
+
+    public static class ScriptItem
+    {
+        Actions action;
+        String value;
+
+        ScriptItem(Actions action, String value)
+        {
+            this.action = action;
+            this.value = value;
+        }
+    }
+
+    private static class TestTunnelAgent extends Thread
+        implements TunnelAgent
+    {
+        ScriptItem[] script;
+        int flags;
+        String error = null;
+        ReadableByteChannel request;
+        WritableByteChannel response;
+
+        final CloseTunnelCallback closeTunnelCallback = () ->
+        {
+            if ((flags & FLAG_ECHO) != 0)
+                System.out.println("TunnelAgent.CloseTunnelCallback");
+        };
+
+        TestTunnelAgent(int flags, ScriptItem[] script)
+        {
+            this.flags = flags;
+            this.script = script;
+        }
+
+        public void joinAndTest()
+        {
+            try
+            {
+                join();
+            }
+            catch (InterruptedException e)
+            {
+                fail("InterruptedException was caught");
+            }
+
+            if (error != null)
+                fail(error);
+        }
+
+        @Override
+        public boolean checkTunnel(String name)
+        {
+            return true;
+        }
+
+        private String readClient(ByteBuffer readBuffer)
+            throws IOException
+        {
+            readBuffer.reset();
+            request.read(readBuffer);
+
+            final int offset = readBuffer.arrayOffset();
+            return new String(readBuffer.array(),
+                offset,
+                readBuffer.position() - offset);
+        }
+
+        private void emulateServer(String serverMessage)
+            throws IOException
+        {
+            final byte[] responseBytes = serverMessage.getBytes();
+            response.write(ByteBuffer.wrap(responseBytes));
+        }
+
+        private void doScriptItem(ScriptItem scriptItem, ByteBuffer readBuffer)
+            throws Exception
+        {
+            switch (scriptItem.action)
+            {
+            case READ_CLIENT:
+                final String actualLine = readClient(readBuffer);
+
+                if ((flags & FLAG_ECHO) != 0)
+                {
+                    System.out.println("SERVER: " + scriptItem.value);
+                    System.out.flush();
+                }
+
+                if (!actualLine.contains(scriptItem.value))
+                {
+                    System.err.println("Expected: " + scriptItem.value);
+                    System.err.println("Actual:   " + actualLine);
+                    System.err.flush();
+
+                    // Unblock the SVN thread by emulating a server error
+                    final String serverError = "( success ( ( ) 0: ) ) ( failure ( ( 160000 39:Test script received unexpected request 0: 0 ) ) ) ";
+                    emulateServer(serverError);
+
+                    fail("Unexpected client request");
+                }
+                break;
+            case EMUL_SERVER:
+                if ((flags & FLAG_ECHO) != 0)
+                {
+                    System.out.println("CLIENT: " + scriptItem.value);
+                    System.out.flush();
+                }
+
+                emulateServer(scriptItem.value);
+                break;
+            case WAIT_TUNNEL:
+                // The loop will end with an exception when tunnel is closed
+                for (;;)
+                {
+                    readClient(readBuffer);
+                }
+            }
+        }
+
+        public void run()
+        {
+            final ByteBuffer readBuffer = ByteBuffer.allocate(1024 * 1024);
+            readBuffer.mark();
+
+            for (ScriptItem scriptItem : script)
+            {
+                try
+                {
+                    doScriptItem(scriptItem, readBuffer);
+                }
+                catch (ClosedChannelException ex)
+                {
+                    // Expected when closed properly
+                }
+                catch (IOException e)
+                {
+                    // IOException occurs when already-freed apr_file_t was lucky
+                    // to have reasonable fields to avoid the crash. It still
+                    // indicates a problem.
+                    error = "IOException was caught in run()";
+                    return;
+                }
+                catch (Throwable t)
+                {
+                    // No other exceptions are expected here.
+                    error = "Exception was caught in run()";
+                    t.printStackTrace();
+                    return;
+                }
+            }
+        }
+
+        @Override
+        public CloseTunnelCallback openTunnel(ReadableByteChannel request,
+                                              WritableByteChannel response,
+                                              String name,
+                                              String user,
+                                              String hostname,
+                                              int port)
+            throws Throwable
+        {
+            this.request = request;
+            this.response = response;
+
+            start();
+
+            if ((flags & FLAG_THROW_IN_OPEN) != 0)
+                throw ClientException.fromException(new RuntimeException("Test exception"));
+
+            return closeTunnelCallback;
+        }
+    };
+
+    /**
+     * Test scenario which previously caused a JVM crash.
+     * In this scenario, GC is invoked before closing tunnel.
+     */
+    public void testCrash_RemoteSession_nativeDispose()
+    {
+        final ScriptItem[] script = new ScriptItem[]
+        {
+            new ScriptItem(Actions.EMUL_SERVER, "( success ( 2 2 ( ) ( edit-pipeline svndiff1 absent-entries commit-revprops depth log-revprops atomic-revprops partial-replay inherited-props ephemeral-txnprops file-revs-reverse ) ) ) "),
+            new ScriptItem(Actions.READ_CLIENT, "edit-pipeline"),
+            new ScriptItem(Actions.EMUL_SERVER, "( success ( ( ANONYMOUS ) 36:0113e071-0208-4a7b-9f20-3038f9caf0f0 ) ) "),
+            new ScriptItem(Actions.READ_CLIENT, "ANONYMOUS"),
+            new ScriptItem(Actions.EMUL_SERVER, "( success ( ) ) ( success ( 36:00000000-0000-0000-0000-000000000000 25:svn+test://localhost/test ( mergeinfo ) ) ) "),
+        };
+
+        final TestTunnelAgent tunnelAgent = new TestTunnelAgent(0, script);
+        final RemoteFactory remoteFactory = new RemoteFactory();
+        remoteFactory.setTunnelAgent(tunnelAgent);
+
+        ISVNRemote remote = null;
+        try
+        {
+            remote = remoteFactory.openRemoteSession("svn+test://localhost/test", 1);
+        }
+        catch (SubversionException e)
+        {
+            fail("SubversionException was caught");
+        }
+
+        // Previously, 'OperationContext::openTunnel()' didn't 'NewGlobalRef()'
+        // callback returned by 'TunnelAgent.openTunnel()'. This caused JVM to
+        // dispose it on next GC. JavaHL calls callback in 'remote.dispose()'.
+        // If the callback was disposed, this caused a JVM crash.
+        System.gc();
+        remote.dispose();
+
+        tunnelAgent.joinAndTest();
+    }
+
+    /**
+     * Test scenario which previously caused a JVM crash.
+     * In this scenario, tunnel was not properly closed after exception in
+     * 'TunnelAgent.openTunnel()'.
+     */
+    public void testCrash_RequestChannel_nativeRead_AfterException()
+    {
+        // Previously, exception caused TunnelChannel's native side to be
+        // destroyed with the following abbreviated stack:
+        //   TunnelChannel.nativeClose()
+        //   svn_pool_destroy(sesspool)
+        //   svn_ra_open5()
+        // TunnelAgent was unaware and called 'RequestChannel.nativeRead()'
+        // or 'ResponseChannel.nativeWrite()', causing either a crash or
+        // an attempt to use a random file.
+        final int flags = FLAG_THROW_IN_OPEN;
+
+        final ScriptItem[] script = new ScriptItem[]
+        {
+            new ScriptItem(Actions.EMUL_SERVER, "( success ( 2 2 ( ) ( edit-pipeline svndiff1 absent-entries commit-revprops depth log-revprops atomic-revprops partial-replay inherited-props ephemeral-txnprops file-revs-reverse ) ) ) "),
+            new ScriptItem(Actions.WAIT_TUNNEL, ""),
+        };
+
+        final TestTunnelAgent tunnelAgent = new TestTunnelAgent(flags, script);
+        final SVNClient svnClient = new SVNClient();
+        svnClient.setTunnelAgent(tunnelAgent);
+
+        try
+        {
+            svnClient.openRemoteSession("svn+test://localhost/test");
+        }
+        catch (SubversionException e)
+        {
+            // RuntimeException("Test exception") is expected here
+        }
+
+        tunnelAgent.joinAndTest();
+    }
+
+    /**
+     * Test scenario which previously caused a JVM crash.
+     * In this scenario, tunnel was not properly closed after an SVN error.
+     */
+    public void testCrash_RequestChannel_nativeRead_AfterSvnError()
+    {
+        final String wcRoot = new File("tempSvnRepo").getAbsolutePath();
+
+        final ScriptItem[] script = new ScriptItem[]
+        {
+            // openRemoteSession
+            new ScriptItem(Actions.EMUL_SERVER, "( success ( 2 2 ( ) ( edit-pipeline svndiff1 absent-entries commit-revprops depth log-revprops atomic-revprops partial-replay inherited-props ephemeral-txnprops file-revs-reverse ) ) ) "),
+            new ScriptItem(Actions.READ_CLIENT, "edit-pipeline"),
+            new ScriptItem(Actions.EMUL_SERVER, "( success ( ( ANONYMOUS ) 36:0113e071-0208-4a7b-9f20-3038f9caf0f0 ) ) "),
+            new ScriptItem(Actions.READ_CLIENT, "ANONYMOUS"),
+            new ScriptItem(Actions.EMUL_SERVER, "( success ( ) ) ( success ( 36:00000000-0000-0000-0000-000000000000 25:svn+test://localhost/test ( mergeinfo ) ) ) "),
+            // checkout
+            new ScriptItem(Actions.READ_CLIENT, "( get-latest-rev ( ) ) "),
+            // Previously, error caused a SubversionException to be created,
+            // which then skipped closing the Tunnel properly due to
+            // 'ExceptionOccurred()' in 'OperationContext::closeTunnel()'.
+            // If TunnelAgent was unaware and called 'RequestChannel.nativeRead()',
+            // it either crashed or tried to use a random file.
+            new ScriptItem(Actions.EMUL_SERVER, "( success ( ( ) 0: ) ) ( failure ( ( 160006 20:This is a test error 0: 0 ) ) ) "),
+            // Pretend that TunnelAgent tries to read more
+            new ScriptItem(Actions.WAIT_TUNNEL, ""),
+        };
+
+        final TestTunnelAgent tunnelAgent = new TestTunnelAgent(0, script);
+        final SVNClient svnClient = new SVNClient();
+        svnClient.setTunnelAgent(tunnelAgent);
+
+        try
+        {
+            svnClient.checkout("svn+test://localhost/test",
+                               wcRoot,
+                               Revision.getInstance(1),
+                               null,
+                               Depth.infinity,
+                               true,
+                               false);
+
+            svnClient.dispose();
+        }
+        catch (ClientException ex)
+        {
+            final int SVN_ERR_FS_NO_SUCH_REVISION = 160006;
+            if (SVN_ERR_FS_NO_SUCH_REVISION != ex.getAllMessages().get(0).getCode())
+                ex.printStackTrace();
+        }
+
+        tunnelAgent.joinAndTest();
+    }
+
     /**
      * @return <code>file</code> converted into a -- possibly
      * <code>canonical</code>-ized -- Subversion-internal path
@@ -4679,6 +5042,7 @@ public class BasicTests extends SVNTests
         return callback.getMessages();
     }
 
+    @SuppressWarnings("deprecation")
     private byte[] collectBlameLines(String path, Revision pegRevision,
                                      Revision revisionStart,
                                      Revision revisionEnd,
@@ -4766,6 +5130,7 @@ public class BasicTests extends SVNTests
     }
 
     /* A blame callback implementation. */
+    @SuppressWarnings("deprecation")
     protected class BlameCallbackImpl implements BlameCallback
     {
 
@@ -4978,6 +5343,158 @@ public class BasicTests extends SVNTests
             }
         }
     }
+
+    /* A blame range callback implementation. */
+    protected class BlameRangeCallbackImpl implements BlameRangeCallback
+    {
+        public long startRevnum = -1;
+        public long endRevnum = -1;
+        public void setRange(long start, long end)
+        {
+            startRevnum = start;
+            endRevnum = end;
+        }
+    }
+
+    /* A blame line callback implementation. */
+    protected class BlameLineCallbackImpl implements BlameLineCallback
+    {
+
+        /** list of blame records (lines) */
+        private List<BlameLine> lines = new ArrayList<BlameLine>();
+
+        public void singleLine(long lineNum, long rev,
+                               Map<String, byte[]> revProps,
+                               long mergedRevision,
+                               Map<String, byte[]> mergedRevProps,
+                               String mergedPath, boolean localChange,
+                               byte[] line)
+            throws ClientException
+        {
+            DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
+
+            try {
+                insertLine(
+                    df.parse(new String(revProps.get("svn:date"))),
+                    rev,
+                    new String(revProps.get("svn:author")),
+                    mergedRevProps == null ? null
+                        : df.parse(new String(mergedRevProps.get("svn:date"))),
+                    mergedRevision,
+                    mergedRevProps == null ? null
+                        : new String(mergedRevProps.get("svn:author")),
+                    mergedPath, line);
+            } catch (ParseException e) {
+                throw ClientException.fromException(e);
+            }
+        }
+
+        private Date getDate(Date date, Date merged_date) {
+            return (merged_date == null ? date : merged_date);
+        }
+
+        private String getAuthor(String author, String merged_author) {
+            return (merged_author == null ? author : merged_author);
+        }
+
+        private long getRevision(long revision, long merged_revision) {
+            return (merged_revision == -1 ? revision : merged_revision);
+        }
+
+        private void insertLine(Date date, long revision, String author,
+                                Date merged_date, long merged_revision,
+                                String merged_author, String merged_path,
+                                byte[] line)
+        {
+            this.lines.add(new BlameLine(getRevision(revision, merged_revision),
+                                         getAuthor(author, merged_author),
+                                         getDate(date, merged_date),
+                                         line));
+        }
+
+        /**
+         * Retrieve the number of line of blame information
+         * @return number of lines of blame information
+         */
+        public int numberOfLines()
+        {
+            return this.lines.size();
+        }
+
+        /**
+         * Retrieve blame information for specified line number
+         * @param i the line number to retrieve blame information about
+         * @return  Returns object with blame information for line
+         */
+        public BlameLine getBlameLine(int i)
+        {
+            if (i >= this.lines.size())
+            {
+                return null;
+            }
+            return this.lines.get(i);
+        }
+
+        /**
+         * Class represeting one line of the lines, i.e. a blame record
+         */
+        public final class BlameLine
+        {
+            private long revision;
+            private String author;
+            private Date changed;
+            private byte[] line;
+
+            /**
+             * Constructor
+             *
+             * @param revision
+             * @param author
+             * @param changed
+             * @param line
+             */
+            public BlameLine(long revision, String author,
+                             Date changed, byte[] line)
+            {
+                this.revision = revision;
+                this.author = author;
+                this.changed = changed;
+                this.line = line;
+            }
+
+            /**
+             * @return Returns the author.
+             */
+            public String getAuthor()
+            {
+                return author;
+            }
+
+            /**
+             * @return Returns the date changed.
+             */
+            public Date getChanged()
+            {
+                return changed;
+            }
+
+            /**
+             * @return Returns the source line content.
+             */
+            public byte[] getLine()
+            {
+                return line;
+            }
+
+            /**
+             * @return Returns the revision.
+             */
+            public long getRevision()
+            {
+                return revision;
+            }
+        }
+    }
 
     /** A helper which calls update with a bunch of default args. */
     private long update(OneTest thisTest)

Modified: subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.java
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.java?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.java (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.java Fri Jan 14 14:01:45 2022
@@ -206,18 +206,17 @@ public class ExceptionTests extends SVNT
         {
             client.blame(thisTest.getWorkingCopy() + "/iota",
                          Revision.getInstance(1), Revision.getInstance(1),
-                         Revision.getInstance(1), false, false,
-                         new BlameCallback()
-                          {
-                            public void singleLine(long lineNum, long revision,
-                                                   Map<String, byte[]> revProps, long mergedRevision,
-                                                   Map<String, byte[]> mergedRevProps,
-                                                   String mergedPath, String line,
-                                                   boolean localChange)
-                            {
-                                throw new TestException("inner", theException);
-                            }
-                          });
+                         Revision.getInstance(1), false, false, null, null,
+                         new BlameLineCallback() {
+                             public void singleLine(long lineNum, long revision,
+                                                    Map<String, byte[]> revProps, long mergedRevision,
+                                                    Map<String, byte[]> mergedRevProps,
+                                                    String mergedPath, boolean localChange,
+                                                    byte[] line)
+                             {
+                                 throw new TestException("inner", theException);
+                             }
+                         });
         }
         catch (ClientException e)
         {

Modified: subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java Fri Jan 14 14:01:45 2022
@@ -1383,9 +1383,9 @@ public class SVNRemoteTests extends SVNT
     {
         ISVNRemote session = getSession();
 
-        Long expected = new Long(1L);
+        Long expected = Long.valueOf(1L);
         ArrayList<Long> revs = new ArrayList<Long>(3);
-        revs.add(new Long(0L));
+        revs.add(Long.valueOf(0L));
         revs.add(expected);
 
         Map<Long, String> locs = session.getLocations("A", 1, revs);

Modified: subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java Fri Jan 14 14:01:45 2022
@@ -224,7 +224,7 @@ class SVNTests extends TestCase
                 rootUrl = rootUrl.replaceFirst("file:/", "file:///");
 
             // According to
-            // http://java.sun.com/j2se/1.5.0/docs/api/java/io/File.html#toURL()
+            // https://docs.oracle.com/javase/1.5.0/docs/api/java/io/File.html#toURL()
             // the URL from rootDir.toURI() may end with a trailing /
             // if rootDir exists and is a directory, so depending if
             // the test suite has been previously run and rootDir

Modified: subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/SVNTests.java
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/SVNTests.java?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/SVNTests.java (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/SVNTests.java Fri Jan 14 14:01:45 2022
@@ -183,7 +183,7 @@ class SVNTests extends TestCase
                 rootUrl = rootUrl.replaceFirst("file:/", "file:///");
 
             // According to
-            // http://java.sun.com/j2se/1.5.0/docs/api/java/io/File.html#toURL()
+            // https://docs.oracle.com/javase/1.5.0/docs/api/java/io/File.html#toURL()
             // the URL from rootDir.toURI() may end with a trailing /
             // if rootDir exists and is a directory, so depending if
             // the test suite has been previously run and rootDir

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/INSTALL
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/INSTALL?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/INSTALL (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/INSTALL Fri Jan 14 14:01:45 2022
@@ -5,7 +5,12 @@ STATUS OF THE SWIG BINDINGS
 * Python
 
   The Python bindings are fairly well developed, although there are some
-  missing parts.
+  missing parts. We support both of Python 2.7 and Python 3.x; however,
+  SWIG Python bindings for different versions of Python cannot be
+  simultaneously installed in the same environment, because they need to
+  install mutually incompatible C shared libraries under the same name.  The
+  distribution tarballs are shipped with SWIG-generated C source files for
+  Python 3.x.
 
   (N.B. As discussed below, they will not compile in Debug mode on Windows.)
 
@@ -64,15 +69,36 @@ STATUS OF THE SWIG BINDINGS
 BUILDING SWIG BINDINGS FOR SVN ON UNIX
 
 
-Step 1:  Install a suitable version of SWIG (which is
-         currently SWIG version 1.3.24 or later).
+Step 1: [Optional] Install a suitable version of SWIG
 
-    * Perhaps your distribution packages a suitable version - if it does
-      install it, and skip to the last bullet point in this section.
+    * SWIG installation is optional.  You do not need to install SWIG
+      if you are using a Subversion distribution tarball because it already
+      contains the source files generated by SWIG.  You will need a suitable
+      version of SWIG if you are using a working copy of Subversion's sources
+      checked out from the repository; if you want to generate the SWIG
+      language bindings C source files by yourself; or if you want to build
+      Python 2.x bindings (since the SWIG-generated C source files in the
+      distribution tarballs target Python 3.x).
+
+    * We currently support SWIG versions 2.0.0 and later, with the
+      following notes:
+      - SWIG 1.3.24 and later 1.3.x may work, but we do not test these
+        versions on our latest source code.
+      - For Python 2 bindings, SWIG 4.0.0 or later is not supported.
+      - For Python 3 bindings, SWIG 3.0.10 or later is required.
+      - Note that SWIG 3.0.9 has some trouble with Python support.
+        (See https://sourceforge.net/p/swig/news/2016/06/swig-3010-released/)
+      - For Perl 5.16 and later, SWIG 2.0.8 or later is required.
+      - For Ruby bindings, SWIG 3.0.8 is not supported.
+
+    * Perhaps your distribution packages a suitable version of SWIG.
+      If so, install it and skip to the last bullet point of this
+      section.
 
     * Go to http://www.swig.org/, download the source tarball, and unpack.
 
-    * In the SWIG-1.3.xx directory, run ./configure.
+    * In the swig-x.y.z, directory, run ./configure (where x.y.z is
+      SWIG version, e.g., 3.0.12).
 
         If you plan to build the Python bindings, and have a system
         with more than one version of Python installed, you may need
@@ -95,43 +121,74 @@ Step 1:  Install a suitable version of S
         Run 'make && make install'
 
     * To verify you have SWIG installed correctly, run "swig -version"
-      from the command line. SWIG should report that it is version 1.3.24
-      or newer.
+      from the command line.  SWIG should report that it is one of the
+      suitable versions mentioned above.
+
+
+Step 1a: Install py3c library if building Python SWIG bindings.
+
+    * Check your distribution packages first for a suitable version, and
+      if found install from there.
+
+    * To install from source, download the latest release from
+      https://github.com/encukou/py3c.
+
+    * This is a header-only library, so no configuring or compiling is
+      necessary, simply 'make install'.
+
 
 Step 2:  Build and Install Subversion.
 
   See Subversion's own INSTALL file for details.
 
-  Make sure that Subversion's ./configure script sees your installed SWIG!
+  If you are using a Subversion distribution tarball and want to rebuild
+  the SWIG language bindings C source files with your installed SWIG, 
+  you need to execute autogen.sh, because the bundled configure script 
+  and makefiles don't support it.
+
+  If you don't use SWIG bindings C source files already generated,
+  make sure that Subversion's ./configure script sees your installed SWIG!
   It tries to detect SWIG near the very end of its output.
+  You can find it by running 'grep "^SWIG=" config.log'.
 
-  Also make sure that the configure script sees the paths to the perl and/or
-  python executable you used to configure SWIG as above.  If it does not then
-  you can specify the correct path by adding PYTHON=/path/to/python or
-  PERL=/path/to/perl onto the command line for configure.  For example:
-       ./configure PYTHON=/usr/bin/python2.7 PERL=/usr/bin/perl5.8.0
+  Also make sure that the configure script sees the paths to the perl,
+  ruby, and/or python executable you used to configure SWIG as above.
+  If it does not then you can specify the correct path by specifying
+  --with-swig-python=/path/to/python, --with-swig-perl=/path/to/perl,
+  and/or --with-swig-ruby=/path/to/ruby to the command line for configure.
+  For example:
+       ./configure --with-swig-python=/usr/bin/python3.7 \
+           --with-swig-perl=/usr/bin/perl5.28.2 \
+           --with-swig-ruby=/usr/bin/ruby2.7
 
   If Subversion's ./configure finds a SWIG that it's happy with, then
   it will build special glue libraries to link svn to the swig bindings:
-    libsvn_swig_py.so (for Python)
-    libsvn_swig_perl.so (for Perl)
-
+    libsvn_swig_py-1.so (for Python)
+    libsvn_swig_perl-1.so (for Perl)
+    libsvn_swig_ruby-1.so (for Ruby)
 
 Step 3:  Install Specific Language Bindings
 
 *  Python
 
-   1.  Run 'make swig-py' from the top of the Subversion build tree,
+   1.  (Optional) If you want to build Python bindings for a version of
+       Python than other than that the prebuilt bindings C sources target
+       (e.g., if you use the Subversion distribution tarball but want to build
+       Python 2 bindings), run 'make clean-swig-py' from the top of the
+       Subversion build tree, to ensure not to use incompatible version of
+       bindings source files.
+
+   2.  Run 'make swig-py' from the top of the Subversion build tree,
        to build the bindings.
 
        (This will invoke SWIG on the *.i files, resulting in a collection
        of .c source files.  It will then compile and link those .c files into
        Python libraries.)
 
-   2.  Run 'make check-swig-py' from the top of the Subversion build
-       tree, to test the bindings 
+   3.  Run 'make check-swig-py' from the top of the Subversion build
+       tree, to test the bindings
 
-   3.  Run 'make install-swig-py' (as root, typically)
+   4.  Run 'make install-swig-py' (as root, typically)
        from the top of the Subversion build tree.  This will copy
        your new Python libraries into the appropriate system location.
 
@@ -145,14 +202,14 @@ Step 3:  Install Specific Language Bindi
        example of doing this for building rpms looks like
        'make install-swig-py DESTDIR=$RPM_BUILD_ROOT/usr'.
 
-   4.  Make sure that whatever directory the bindings got installed in
+   5.  Make sure that whatever directory the bindings got installed in
        is in your Python search path.  That directory depends on how you
        installed; a typical location is /usr/local/lib/svn-python/.
 
        There are several ways to do this. See Python's documentation for
        'sys.path' and 'PYTHONPATH'. A nice way to do this is:
        $ echo /usr/local/lib/svn-python \
-           > /usr/lib/python2.x/site-packages/subversion.pth
+           > /usr/lib/python3.x/site-packages/subversion.pth
 
        You may also need to update your operating system's dynamic linker
        configuration to enable Python to load these new libraries. On some
@@ -162,15 +219,15 @@ Step 3:  Install Specific Language Bindi
 *  Perl
 
    Perl 5.8.0 is required.  You can specify the perl binary by passing
-   PERL=/path/to/perl as part of the configure command in the top level
-   of the Subversion source tree.  Make sure that the Perl version used
-   is the same one that you configured SWIG to run against during the
-   SWIG configure (see above).
+   --with-swig-perl=/path/to/perl as an option to the configure command
+   in the top level of the Subversion source tree.  Make sure that the
+   Perl version used is the same one that you configured SWIG to run
+   against during the SWIG configure (see above).
 
    1.  Run `make swig-pl' from the top of the Subversion build tree.
 
    2.  Run `make check-swig-pl' from the top of the Subversion build
-       tree, to test the bindings 
+       tree, to test the bindings
 
    3.  to install run `make install-swig-pl' from the top of the
        Subversion build tree.
@@ -182,9 +239,9 @@ Step 3:  Install Specific Language Bindi
 
    2.  Run `make install-swig-pl-lib'
 
-   3.  cd subversion/bindings/swig/perl/native 
+   3.  cd subversion/bindings/swig/perl/native
 
-   4.  Run `perl Makefile.PL EXTRAOPTIONSHERE` 
+   4.  Run `perl Makefile.PL EXTRAOPTIONSHERE`
 
    5.  Run `make install'
 
@@ -194,21 +251,22 @@ Step 3:  Install Specific Language Bindi
 
 
 * Ruby
-  
+
    1. Run `make swig-rb' from the top of the Subversion build tree,
       to build the bindings.
 
    2. Run `make check-swig-rb' from the top of the Subversion build
       tree, to test the bindings.
 
-   3. To install, run `make install-swig-rb' from the top of the 
+   3. To install, run `make install-swig-rb' from the top of the
       Subversion build tree.
 
-   You can specify the ruby binary by passing RUBY=/path/to/ruby as part 
-   of the configure command in the top level of the Subversion source 
-   tree. Make sure that the Ruby version used is the same one that you 
-   configured SWIG to run against during the SWIG configure (see above). 
-   
+   You can specify the ruby binary by passing --with-swig-ruby=/path/to/ruby
+   as an option to the configure command in the top level of the
+   Subversion source tree. Make sure that the Ruby version used is
+   the same one that you configured SWIG to run against during the
+   SWIG configure (see above).
+
 
 BUILDING SWIG BINDINGS FOR SVN ON WINDOWS
 
@@ -226,6 +284,9 @@ BUILDING SWIG BINDINGS FOR SVN ON WINDOW
            http://www.python.org/
            http://www.activestate.com/ActivePerl/
 
+       If you plan to build python bindings, you will also need to download and
+       extract the py3c library from https://github.com/encukou/py3c/releases.
+
        NOTE: Our Python SWIG bindings will currently NOT compile in Debug mode
              unless you have python24_d.lib (which binary distributions of
              Python do not contain).  Therefore, the Python bindings will only
@@ -242,6 +303,11 @@ BUILDING SWIG BINDINGS FOR SVN ON WINDOW
 
         > gen-make.py <other options> --with-swig="C:\Program Files\SWIG-2.0.2"
 
+       If you are building python bindings, point to the py3c directory as well:
+
+        > gen-make.py <other options> --with-swig="C:\Program Files\SWIG-2.0.2" \
+                                      --with-py3c="C:\SVN\py3c"
+
    4.  If you haven't already built Subversion, you should do so now.
        Instructions are in the main INSTALL file.
 
@@ -324,12 +390,12 @@ TESTING SWIG BINDINGS
 *  Perl
 
    The Perl bindings are using the standard module testing facilities
-   to do regression tests. Simply run 'make check-swig-pl' as described in 
+   to do regression tests. Simply run 'make check-swig-pl' as described in
    the install section.
 
 *  Ruby
 
-   To test the Ruby bindings, simply run `make check-swig-rb' as described 
+   To test the Ruby bindings, simply run `make check-swig-rb' as described
    in the install section.
 
 
@@ -361,7 +427,7 @@ USING SWIG BINDINGS
    sample/demo programs found in tools/examples/ in the Subversion
    source code tree.  Additionally, there are several third-party
    tools that make use of these bindings, including ViewVC
-   (http://viewvc.tigris.org/) and Trac (http://trac.edgewall.org/).
+   (http://www.viewvc.org/) and Trac (https://trac.edgewall.org/).
 
 *  Perl
 

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/core.i
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/core.i?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/core.i (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/core.i Fri Jan 14 14:01:45 2022
@@ -23,16 +23,16 @@
  *   of the more specific module files.
  */
 
+%include svn_global.swg
+
 #if defined(SWIGPYTHON)
-%module(package="libsvn") core
+%module(package="libsvn", moduleimport=SVN_PYTHON_MODULEIMPORT) core
 #elif defined(SWIGPERL)
 %module "SVN::_Core"
 #elif defined(SWIGRUBY)
 %module "svn::ext::core"
 #endif
 
-%include svn_global.swg
-
 %{
 #include <apr.h>
 #include <apr_general.h>
@@ -362,7 +362,9 @@
     /* svn_config_get */
     const char *default_value,
     /* svn_config_read_auth_data */
-    const char *config_dir, 
+    const char *config_dir,
+    /* svn_config_get_user_config_path */
+    const char *fname,
     /* svn_diff_file_output_merge */
     const char *conflict_original,
     const char *conflict_modified,
@@ -381,6 +383,7 @@
     if (PyLong_Check($input)) {
         temp = PyLong_AsUnsignedLong($input);
     }
+%#if IS_PY3 != 1
     else if (PyInt_Check($input)) {
         /* wish there was a PyInt_AsUnsignedLong but there isn't
            the mask version doesn't do bounds checking for us.
@@ -389,6 +392,7 @@
            problem goes away because PyInt is gone anyway. */
         temp = PyInt_AsUnsignedLongMask($input);
     }
+%#endif
     else {
         PyErr_SetString(PyExc_TypeError,
                         "expecting an integer for the buffer size");
@@ -418,7 +422,7 @@
 
 #ifdef SWIGPYTHON
 %typemap(argout) (char *buffer, apr_size_t *len) {
-  %append_output(PyString_FromStringAndSize($1, *$2));
+  %append_output(PyBytes_FromStringAndSize($1, *$2));
   free($1);
 }
 #endif
@@ -440,13 +444,24 @@
 */
 #ifdef SWIGPYTHON
 %typemap(in) (const char *data, apr_size_t *len) ($*2_type temp) {
-    if (!PyString_Check($input)) {
+    Py_ssize_t length;
+    if (PyBytes_Check($input)) {
+        if (PyBytes_AsStringAndSize($input, (char **)&$1, &length) == -1) {
+            SWIG_fail;
+        }
+    }
+    else if (PyUnicode_Check($input)) {
+        $1 = (char *)PyStr_AsUTF8AndSize($input, &length);
+        if (PyErr_Occurred()) {
+            SWIG_fail;
+        }
+    }
+    else {
         PyErr_SetString(PyExc_TypeError,
-                        "expecting a string for the buffer");
+                        "expecting a bytes or str object for the buffer");
         SWIG_fail;
     }
-    $1 = PyString_AS_STRING($input);
-    temp = PyString_GET_SIZE($input);
+    temp = ($*2_type)length;
     $2 = ($2_ltype)&temp;
 }
 #endif
@@ -499,8 +514,8 @@
        SWIG_fail;
     }
 
-    if (PyString_Check($input)) {
-        char *value = PyString_AS_STRING($input);
+    if (PyBytes_Check($input)) {
+        const char *value = PyBytes_AsString($input);
         $1 = apr_pstrdup(_global_pool, value);
     }
     else if (PyLong_Check($input)) {
@@ -605,7 +620,7 @@
 */
 #ifdef SWIGPYTHON
 %typemap(in) FILE * {
-    $1 = PyFile_AsFile($input);
+    $1 = svn_swig_py_as_file($input);
     if ($1 == NULL) {
         PyErr_SetString(PyExc_ValueError, "Must pass in a valid file object");
         SWIG_fail;
@@ -710,11 +725,6 @@ core_set_current_pool (apr_pool_t *pool)
                   svn_swig_rb_config_section_enumerator)
 #endif
 
-/* Allow None to be passed as config_dir argument */
-#ifdef SWIGPYTHON
-%typemap(in,parse="z") const char *config_dir "";
-#endif
-
 /* -----------------------------------------------------------------------
   thunk the various authentication prompt functions.
   PERL NOTE: store the inputed SV in _global_callback for use in the
@@ -816,6 +826,7 @@ core_set_current_pool (apr_pool_t *pool)
 
 %include svn_error_codes_h.swg
 %include svn_time_h.swg
+%include svn_types_impl_h.swg
 %include svn_types_h.swg
 %include svn_pools_h.swg
 %include svn_version_h.swg
@@ -827,6 +838,7 @@ core_set_current_pool (apr_pool_t *pool)
 %include svn_props_h.swg
 #pragma SWIG nowarn=+305
 
+%include svn_opt_impl_h.swg
 %include svn_opt_h.swg
 %include svn_cmdline_h.swg
 %include svn_auth_h.swg

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/include/proxy.py
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/include/proxy.py?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/include/proxy.py (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/include/proxy.py Fri Jan 14 14:01:45 2022
@@ -12,23 +12,57 @@
     if "_is_valid" in self.__dict__:
       assert self.__dict__["_is_valid"](), "Variable has already been deleted"
 
-  def __getattr__(self, name):
-    """Get an attribute from this object"""
-    self.assert_valid()
-
-    value = _swig_getattr(self, self.__class__, name)
-
-    # If we got back a different object than we have, we need to copy all our
-    # metadata into it, so that it looks identical
-    members = self.__dict__.get("_members")
-    if members is not None:
-      _copy_metadata_deep(value, members.get(name))
+  def _retrieve_swig_value(self, name, value):
+    # If we got back a different object than we have cached, we need to copy
+    # all our metadata into it, so that it looks identical to the one
+    # originally set.
+    members = self.__dict__.get('_members')
+    if members is not None and name in members:
+      _copy_metadata_deep(value, members[name])
 
     # Verify that the new object is good
     _assert_valid_deep(value)
 
     return value
 
+  # Attribute access must be intercepted to ensure that objects coming from
+  # read attribute access match those that are set with write attribute access.
+  # Specifically the metadata, such as the associated apr_pool object, should
+  # match the originally assigned object.
+  #
+  # For classic classes it is enough to use __getattr__ to intercept swig
+  # derived attributes. However, with new style classes SWIG makes use of
+  # descriptors which mean that __getattr__ is never called. Therefore,
+  # __getattribute__ must be used for the interception.
+
+  if _newclass:
+    def __getattribute__(self, name):
+      """Manage access to all attributes of this object."""
+
+      # Start by mimicking __getattr__ behavior: immediately return __dict__ or
+      # items directly present in __dict__
+      mydict = object.__getattribute__(self, '__dict__')
+
+      if name == "__dict__":
+        return mydict
+
+      if name in mydict:
+        return mydict[name]
+
+      object.__getattribute__(self, 'assert_valid')()
+
+      value = _get_instance_attr(self, name)
+      fn = object.__getattribute__(self, '_retrieve_swig_value')
+      return fn(name, value)
+  else:
+    def __getattr__(self, name):
+      """Get an attribute from this object"""
+      self.assert_valid()
+
+      value = _swig_getattr(self, self.__class__, name)
+
+      return self._retrieve_swig_value(name, value)
+
   def __setattr__(self, name, value):
     """Set an attribute on this object"""
     self.assert_valid()
@@ -38,4 +72,4 @@
     # SWIG-land
     self.__dict__.setdefault("_members",{})[name] = value
 
-    return _swig_setattr(self, self.__class__, name, value)
+    return _set_instance_attr(self, name, value)

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/include/proxy.swg
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/include/proxy.swg?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/include/proxy.swg (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/include/proxy.swg Fri Jan 14 14:01:45 2022
@@ -36,8 +36,8 @@
     if value is None or old_value is None or value is old_value: return
     
     if isinstance(value, dict):
-      for k, v in value.iteritems():
-        _copy_metadata_deep(v, old_value[k])
+      for k in value:
+        _copy_metadata_deep(value[k], old_value[k])
     elif isinstance(value, list):
       for v, old_v in zip(value, old_value):
         _copy_metadata_deep(v, old_v)
@@ -50,15 +50,72 @@
   def _assert_valid_deep(value):
     """Assert value's validity, recursively traversing lists and dicts."""
     if isinstance(value, dict):
-      for v in value.itervalues():
-        _assert_valid_deep(v)
+      for k in value:
+        _assert_valid_deep(value[k])
     elif isinstance(value, list):
       for v in value:
         _assert_valid_deep(v)
-    else:
-      if hasattr(value, "assert_valid"):
-        value.assert_valid()
+    # Ensure that the passed in value isn't a type, which could have an
+    # assert_valid attribute, but it can not be called without an instance.
+    elif type(value) != type:
+      try:
+        fn = value.assert_valid
+      except AttributeError:
+        pass
+      else:
+        fn()
+
+%}
+#if defined(SWIGPYTHON_PY3)
+#if SWIG_VERSION >= 0x040000
+%pythoncode %{
+  # -classic and -modern options have been dropped and this variable
+  # is not generated since SWIG 4
+  _newclass = 1
+  _get_instance_attr = object.__getattribute__
+  _set_instance_attr = _swig_setattr_nondynamic_instance_variable(object.__setattr__)
+
+%}
+#else
+%pythoncode %{
+  # SWIG classes generated with -modern do not define this variable
+  try:
+    _newclass
+  except NameError:
+    _newclass = 1
+  else:
+    raise RuntimeError("Require -modern option, but _newclass is defined")
+
+  _get_instance_attr = object.__getattribute__
+  _set_instance_attr = _swig_setattr_nondynamic_method(object.__setattr__)
+
+%}
+#endif
+#else
+%pythoncode %{
+  # SWIG classes generated with -classic do not define this variable,
+  # so set it to 0 when it doesn't exist
+  try:
+    _newclass
+  except NameError:
+    _newclass = 0
+
+  if _newclass:
+    def _get_instance_attr(self, name):
+      try:
+        return object.__getattribute__(self, name)
+      except AttributeError:
+        return _swig_getattr(self, object.__getattribute__(self, '__class__'),
+                             name)
+  else:
+    def _get_instance_attr(self, name):
+      return _swig_getattr(self, self.__class__, name)
+
+  def _set_instance_attr(self, name, value):
+    return _swig_setattr(self, self.__class__, name, value)
+
 %}
+#endif
 
 /* Default code for all wrapped proxy classes in Python.
  * Inline the code from a separate file to avoid issues with

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/include/proxy_apr.swg
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/include/proxy_apr.swg?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/include/proxy_apr.swg (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/include/proxy_apr.swg Fri Jan 14 14:01:45 2022
@@ -84,6 +84,7 @@ class GenericSWIGWrapper:
   def set_parent_pool(self, pool):
     """Set the parent pool of this object"""
     self._parent_pool = pool
+    self._is_valid = weakref.ref(pool._is_valid)
 
   def valid(self):
     """Is this object valid?"""
@@ -99,8 +100,14 @@ class GenericSWIGWrapper:
     return self.this
 
 def _mark_weakpool_invalid(weakpool):
-  if weakpool and weakpool() and hasattr(weakpool(), "_is_valid"):
-    del weakpool()._is_valid
+  if weakpool:
+    pool = weakpool()
+    if pool:
+      try:
+        del pool._is_valid
+      except AttributeError:
+        pass
+       
 
 %}
 
@@ -133,7 +140,17 @@ struct apr_pool_t {
       def valid(self):
         """Check whether this memory pool and its parents
         are still valid"""
-        return hasattr(self,"_is_valid")
+        try:
+          self._is_valid
+        except AttributeError:
+          return False
+        # We must check whether the parent pool is valid even if
+        # the pool is valid because weakref's callback is not
+        # invoked when it is finalized by cyclic garbage collector
+        if self._parent_pool:
+          return self._parent_pool.valid()
+        else:
+          return True
 
       def assert_valid(self):
         """Assert that this memory_pool is still valid."""
@@ -164,10 +181,15 @@ struct apr_pool_t {
           self._svn_swig_py_clear_application_pool()
 
         # Mark self as invalid
-        if hasattr(self, "_parent_pool"):
+        try:
           del self._parent_pool
-        if hasattr(self, "_is_valid"):
+        except AttributeError:
+          pass
+
+        try:
           del self._is_valid
+        except AttributeError:
+          pass
 
       def __del__(self):
         """Automatically destroy memory pools, if necessary"""
@@ -200,8 +222,11 @@ struct apr_pool_t {
       def _wrap(self, obj):
         """Mark a SWIG object as owned by this pool"""
         self.assert_valid()
-        if hasattr(obj, "set_parent_pool"):
-          obj.set_parent_pool(self)
+        
+        fn = getattr(obj, 'set_parent_pool', None)
+
+        if fn is not None:
+          fn(self)
           return obj
         elif obj is None:
           return None

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_containers.swg
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_containers.swg?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_containers.swg (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_containers.swg Fri Jan 14 14:01:45 2022
@@ -527,7 +527,10 @@
   apr_array_header_t **logfiles,
   apr_array_header_t **names_p,
   apr_array_header_t **targets_p,
-  apr_array_header_t **args_p
+  apr_array_header_t **args_p,
+  apr_array_header_t **props_conflicted,
+  apr_array_header_t **possible_moved_to_repos_relpaths,
+  apr_array_header_t **possible_moved_to_abspaths
 };
 
 /* -----------------------------------------------------------------------
@@ -577,6 +580,40 @@
 }
 #endif
 
+/* -----------------------------------------------------------------------
+   apr_array_header_t **options
+   For svn_client_conflict_option_t
+*/
+
+#ifdef SWIGPYTHON
+%typemap(argout) apr_array_header_t **options {
+  %append_output
+    (svn_swig_py_pointerlist_to_list(*$1,
+                                     $descriptor(svn_client_conflict_option_t *),
+                                     _global_py_pool));
+  if (PyErr_Occurred()) {
+    SWIG_fail;
+  }
+}
+#endif
+
+/* -----------------------------------------------------------------------
+   apr_array_header_t **versions_p
+   For svn_client__shelf_get_all_versions
+*/
+
+#ifdef SWIGPYTHON
+%typemap(argout) apr_array_header_t **versions_p {
+  %append_output
+    (svn_swig_py_pointerlist_to_list(*$1,
+                                     $descriptor(svn_client__shelf_version_t *),
+                                     _global_py_pool));
+  if (PyErr_Occurred()) {
+    SWIG_fail;
+  }
+}
+#endif
+
 /* =======================================================================
    %typemap(in) apr_array_header_t *
 */

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_global.swg
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_global.swg?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_global.swg (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_global.swg Fri Jan 14 14:01:45 2022
@@ -31,6 +31,17 @@
 #define SVN_DEPRECATED
 #endif
 
+#ifdef SWIGPYTHON
+%begin %{
+#define SWIG_PYTHON_STRICT_BYTE_CHAR
+#if defined(_MSC_VER)
+/* Prevent "non-constant aggregate initializer" errors from Python.h in
+ * Python 3.9 */
+# pragma warning(default : 4204)
+#endif
+%}
+#endif
+
 %include typemaps.i
 %include constraints.i
 %include exception.i
@@ -54,6 +65,7 @@
 #ifdef SWIGPYTHON
 %{
 #include "swigutil_py.h"
+#include "swigutil_py3c.h"
 %}
 #endif
 #ifdef SWIGPERL
@@ -135,9 +147,8 @@ static PyObject * _global_py_pool = NULL
 /* Python format specifiers. Use Python instead of SWIG to parse
    these basic types, because Python reports better error messages
    (with correct argument numbers). */
-%typemap (in, parse="s")
-  char *, char const *, char * const, char const * const "";
 %typemap (in, parse="c") char "";
+
 %typemap (in, fragment=SWIG_As_frag(long)) long
 {
   $1 = ($1_ltype)SWIG_As(long)($input);
@@ -236,3 +247,40 @@ static VALUE *_global_vresult_address =
 /* Now, include the main Subversion typemap library. */
 %include svn_types.swg
 %include proxy.swg
+
+
+#ifdef SWIGPYTHON
+/* Since Python 3.8+ on Windows, DLL dependencies when loading *.pyd file
+ * searches only the system paths, the directory containing the *.pyd file and
+ * the directories added with os.add_dll_directory().
+ * See also https://bugs.python.org/issue36085.
+ */
+%define SVN_PYTHON_MODULEIMPORT
+"
+def _dll_paths():
+    import os
+    if hasattr(os, 'add_dll_directory'):  # Python 3.8+ on Windows
+        cookies = []
+        for path in os.environ.get('PATH', '').split(os.pathsep):
+            if path and os.path.isabs(path):
+                try:
+                    cookie = os.add_dll_directory(path)
+                except OSError:
+                    continue
+                else:
+                    cookies.append(cookie)
+        return cookies
+    else:
+        return ()
+
+_dll_paths = _dll_paths()
+try:
+    from . import $module
+finally:
+    _dll_path = None
+    for _dll_path in _dll_paths:
+        _dll_path.close()
+    del _dll_paths, _dll_path
+"
+%enddef
+#endif

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_string.swg
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_string.swg?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_string.swg (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_string.swg Fri Jan 14 14:01:45 2022
@@ -36,9 +36,8 @@ typedef struct svn_string_t svn_string_t
     if (*$1 == NULL) {
         Py_INCREF(Py_None);
         s = Py_None;
-    }
-    else {
-        s = PyString_FromStringAndSize((*$1)->data, (*$1)->len);
+    } else {
+        s = PyBytes_FromStringAndSize((*$1)->data, (*$1)->len);
         if (s == NULL)
             SWIG_fail;
     }
@@ -75,14 +74,33 @@ typedef struct svn_string_t svn_string_t
 
 #ifdef SWIGPYTHON
 %typemap(in) svn_stringbuf_t * {
-    if (!PyString_Check($input)) {
-        PyErr_SetString(PyExc_TypeError, "not a string");
-        SWIG_fail;
-    }
-    $1 = svn_stringbuf_ncreate(PyString_AS_STRING($input),
-                               PyString_GET_SIZE($input),
-                               /* ### gah... what pool to use? */
-                               _global_pool);
+    if ($input == Py_None) {
+        $1 = NULL;
+    }
+    else {
+        Py_ssize_t strBufLen;
+        char *strBufChar;
+        if (PyBytes_Check($input)) {
+            if (-1 == PyBytes_AsStringAndSize($input, &strBufChar,
+                                              &strBufLen)) {
+                SWIG_fail;
+            }
+        }
+        else if (PyUnicode_Check($input)) {
+            strBufChar = (char *)PyStr_AsUTF8AndSize($input, &strBufLen);
+            if (PyErr_Occurred()) {
+                SWIG_fail;
+            }
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError,
+                            "not a bytes, str or None object");
+            SWIG_fail;
+        }
+        $1 = svn_stringbuf_ncreate((const char *)strBufChar, strBufLen,
+                                   /* ### gah... what pool to use? */
+                                   _global_pool);
+    }
 }
 #endif
 
@@ -139,7 +157,7 @@ typedef struct svn_string_t svn_string_t
 
 #ifdef SWIGPYTHON
 %typemap(out) svn_stringbuf_t * {
-    $result = PyString_FromStringAndSize($1->data, $1->len);
+    $result = PyBytes_FromStringAndSize($1->data, (Py_ssize_t)($1->len));
 }
 #endif
 
@@ -171,15 +189,29 @@ typedef struct svn_string_t svn_string_t
 /* const svn_string_t * is always an input parameter */
 #ifdef SWIGPYTHON
 %typemap(in) const svn_string_t * (svn_string_t value) {
-    if ($input == Py_None)
+    if ($input == Py_None) {
         $1 = NULL;
+    }
     else {
-        if (!PyString_Check($input)) {
-            PyErr_SetString(PyExc_TypeError, "not a string");
+        Py_ssize_t pyStrLen;
+        if (PyBytes_Check($input)) {
+            if (PyBytes_AsStringAndSize($input, (char **)&(value.data),
+                                        &pyStrLen) == -1) {
+                SWIG_fail;
+            }
+        }
+        else if (PyUnicode_Check($input)) {
+            value.data = PyStr_AsUTF8AndSize($input, &pyStrLen);
+            if ((value.data == NULL) || PyErr_Occurred()) {
+                SWIG_fail;
+            }
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError,
+                            "not a bytes, str, or None object");
             SWIG_fail;
         }
-        value.data = PyString_AS_STRING($input);
-        value.len = PyString_GET_SIZE($input);
+        value.len = pyStrLen;
         $1 = &value;
     }
 }
@@ -223,7 +255,7 @@ typedef struct svn_string_t svn_string_t
 
 #ifdef SWIGPYTHON
 %typemap(out) svn_string_t * {
-    $result = PyString_FromStringAndSize($1->data, $1->len);
+    $result = PyBytes_FromStringAndSize($1->data, $1->len);
 }
 #endif
 #ifdef SWIGPERL
@@ -242,6 +274,26 @@ typedef struct svn_string_t svn_string_t
 }
 #endif
 
+ /* -----------------------------------------------------------------------
+   Type: char * (input)
+*/
+#ifdef SWIGPYTHON
+%typemap (in) IN_STRING
+{
+    $1 = svn_swig_py_string_to_cstring($input, FALSE, "$symname", "$1_name");
+    if (PyErr_Occurred()) SWIG_fail;
+}
+
+%typemap (freearg) IN_STRING "";
+
+%apply IN_STRING {
+    const char *,
+    char *,
+    char const *,
+    char * const,
+    char const * const
+};
+#endif
 /* -----------------------------------------------------------------------
    define a way to return a 'const char *'
 */
@@ -253,7 +305,7 @@ typedef struct svn_string_t svn_string_t
         s = Py_None;
     }
     else {
-        s = PyString_FromString(*$1);
+        s = PyBytes_FromString(*$1);
         if (s == NULL)
             SWIG_fail;
     }
@@ -283,4 +335,8 @@ typedef struct svn_string_t svn_string_t
 #endif
 
 /* svn_wc_get_ancestry() lacks a 'const' */
-%apply const char **OUTPUT { const char **, char **url };
+%apply const char **OUTPUT {
+  const char **,
+  char **url,
+  char **log_message
+};

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_swigcompat.swg
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_swigcompat.swg?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_swigcompat.swg (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_swigcompat.swg Fri Jan 14 14:01:45 2022
@@ -45,7 +45,7 @@
 #if SWIG_VERSION <= 0x010327
 #ifdef SWIGPYTHON
 %define %set_constant(name, value)
-PyDict_SetItemString(d, name, value);
+PyDict_SetItem(d, PyBytes_FromString(name), value);
 %enddef
 #endif
 #endif

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_types.swg
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_types.swg?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_types.swg (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/include/svn_types.swg Fri Jan 14 14:01:45 2022
@@ -93,6 +93,7 @@
 %apply SWIGTYPE **OUTPARAM {
   /* apr */
   apr_file_t **,
+  apr_hash_t **,
   /* svn_types.h */
   svn_commit_info_t **,
   svn_dirent_t **,
@@ -116,10 +117,16 @@
   void **credentials,
   void **iter_baton,
   void **token,
+  /* svn_checksum */
+  svn_checksum_t **,
   /* svn_client */
   svn_client_commit_info_t **,
+  svn_client_conflict_t **,
+  svn_client_conflict_option_t **,
   svn_client_ctx_t **,
   const svn_client_commit_item3_t **,
+  svn_client__shelf_t **,
+  svn_client__shelf_version_t **,
   /* svn_delta */
   const svn_delta_editor_t **,
   svn_txdelta_stream_t **,
@@ -129,6 +136,7 @@
   svn_txdelta_window_handler_t *,
 #endif
   void **handler_baton,
+  void **handler2_baton,
   void **root_baton,
   void **child_baton,
   void **file_baton,
@@ -142,6 +150,9 @@
   svn_fs_root_t **,
   svn_fs_txn_t **,
   void **contents_baton_p,
+  /* svn_io */
+  svn_io_dirent2_t **,
+  svn_stream_mark_t **,
   /* svn_ra */
   svn_ra_callbacks2_t **,
   svn_ra_plugin_t **,
@@ -335,7 +346,11 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE se
    Create a typemap for specifying string args that may be NULL.
 */
 #ifdef SWIGPYTHON
-%typemap(in, parse="z") const char *MAY_BE_NULL "";
+%typemap(in) const char *MAY_BE_NULL
+{
+    $1 = svn_swig_py_string_to_cstring($input, TRUE, "$symname", "$1_name");
+    if (PyErr_Occurred()) SWIG_fail;
+}
 #endif
 
 #ifdef SWIGPERL
@@ -423,6 +438,55 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE se
     Py_INCREF(Py_None);
     $result = Py_None;
 }
+
+%typemap(out) svn_error_t * SVN_ERR_WITH_ATTRS (apr_status_t apr_err,
+                                                PyObject *exc_class,
+                                                PyObject *exc_ob) {
+    apr_err = 0;
+    exc_class = exc_ob = NULL;
+    if ($1 != NULL)
+      {
+        apr_err = $1->apr_err;
+        if (apr_err != SVN_ERR_SWIG_PY_EXCEPTION_SET)
+          {
+            svn_swig_py_build_svn_exception(&exc_class, &exc_ob, $1);
+            if (exc_ob == NULL)
+              {
+                /* We couldn't get an exception instance. */
+                if (exc_class != NULL)
+                  {
+                    /* Raise an exception without instance ... */
+                    PyErr_SetNone(exc_class);
+                    Py_DECREF(exc_class);
+                  }
+                SWIG_fail;
+              }
+            /* We have an exeception instance, but we don't raise it until
+               typemap(ret) block after typemap(argout) blocks. */
+          }
+        else
+          {
+            svn_error_clear($1);
+            SWIG_fail;
+          }
+      }
+    else
+      {
+        Py_INCREF(Py_None);
+        $result = Py_None;
+      }
+}
+
+%typemap(ret) svn_error_t * SVN_ERR_WITH_ATTRS {
+    if (exc_ob != NULL)
+      {
+        PyErr_SetObject(exc_class, exc_ob);
+        Py_DECREF(exc_class);
+        Py_DECREF(exc_ob);
+        Py_XDECREF($result);
+        SWIG_fail;
+      }
+}
 #endif
 
 #ifdef SWIGPERL
@@ -464,12 +528,23 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE se
 */
 #ifdef SWIGPYTHON
 %typemap(in) (const char *PTR, apr_size_t LEN) {
-    if (!PyString_Check($input)) {
-        PyErr_SetString(PyExc_TypeError, "expecting a string");
+    Py_ssize_t pyStrLen;
+    if (PyBytes_Check($input)) {
+        if (PyBytes_AsStringAndSize($input, (char **)&$1, &pyStrLen) == -1) {
+            SWIG_fail;
+        }
+    }
+    else if (PyUnicode_Check($input)) {
+        $1 = (char *)PyStr_AsUTF8AndSize($input, &pyStrLen);
+        if (PyErr_Occurred()) {
+            SWIG_fail;
+        }
+    }
+    else {
+        PyErr_SetString(PyExc_TypeError, "expecting a bytes or str");
         SWIG_fail;
     }
-    $1 = PyString_AS_STRING($input);
-    $2 = PyString_GET_SIZE($input);
+    $2 = pyStrLen;
 }
 #endif
 
@@ -941,7 +1016,15 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE se
 
 #ifdef SWIGPYTHON
 %typemap(in) svn_stream_t *WRAPPED_STREAM {
-    $1 = svn_swig_py_make_stream ($input, _global_pool);
+    if ($input == Py_None) {
+        $1 = NULL;
+    }
+    else {
+        $1 = svn_swig_py_make_stream ($input, _global_pool);
+        if ($1 == NULL) {
+            SWIG_fail;
+        }
+    }
 }
 #endif
 
@@ -1094,7 +1177,7 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE se
 #ifdef SWIGPYTHON
 %typemap(argout) unsigned char digest[ANY]
 {
-  %append_output(PyString_FromStringAndSize((char *)$1, APR_MD5_DIGESTSIZE));
+  %append_output(PyBytes_FromStringAndSize((const char *)$1, APR_MD5_DIGESTSIZE));
 }
 #endif
 
@@ -1167,7 +1250,7 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE se
     if ($input == Py_None) {
         $1 = NULL;
     } else {
-        $1 = (unsigned char *) PyString_AsString($input);
+        $1 = (unsigned char *) PyBytes_AsString($input);
         if ($1 == NULL) SWIG_fail;
     }
 }

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c Fri Jan 14 14:01:45 2022
@@ -284,7 +284,7 @@ apr_array_header_t *svn_swig_pl_objs_to_
 
 /* Convert a single revision range or an array of revisions ranges
  * Note: We can't simply use svn_swig_pl_to_array() as is, since
- * it immediatley checks whether source is an array reference and then
+ * it immediately checks whether source is an array reference and then
  * proceeds to treat this as the "array of ..." case. But a revision range
  * may be specified as a (two-element) array. Hence we first try to
  * convert source as a single revision range. Failing that and if it's
@@ -591,7 +591,7 @@ svn_error_t *svn_swig_pl_callback_thunk(
         case 'L': /* apr_int64_t */
             /* Pass into perl as a string because some implementations may
              * not be able to handle a 64-bit int.  If it's too long to
-             * fit in Perl's interal IV size then perl will only make
+             * fit in Perl's internal IV size then perl will only make
              * it available as a string.  If not then perl will convert
              * it to an IV for us.  So this handles the problem gracefully */
             c = malloc(30);
@@ -634,7 +634,7 @@ svn_error_t *svn_swig_pl_callback_thunk(
       count = call_method(func, call_flags );
         break;
     default:
-      croak("unkonwn calling type");
+      croak("unknown calling type");
         break;
     }
     SPAGAIN ;

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/perl/native/Client.pm
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/perl/native/Client.pm?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/perl/native/Client.pm (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/perl/native/Client.pm Fri Jan 14 14:01:45 2022
@@ -1029,7 +1029,7 @@ is updated.
 If $ignore_externals is set, don't process externals definitions as part of
 this operation.
 
-If $depth is $SVN::Depth::infinity, update fully recursivelly.  Else if it is
+If $depth is $SVN::Depth::infinity, update fully recursively.  Else if it is
 $SVN::Depth::immediates or $SVN::Depth::files, update each target and its file
 entries, but not its subdirectories.  Else if $SVN::Depth::empty, update
 exactly each target, nonrecursively (essentially, update the target's

Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/perl/native/Core.pm
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/perl/native/Core.pm?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/bindings/swig/perl/native/Core.pm (original)
+++ subversion/branches/multi-wc-format/subversion/bindings/swig/perl/native/Core.pm Fri Jan 14 14:01:45 2022
@@ -784,7 +784,7 @@ $SVN::Depth::infinity.
 
 Exclude (i.e., don't descend into) directory D.
 
-Note: In Subversion 1.5, $SVN::Depth::exclude is B<not> supported anyhwere in
+Note: In Subversion 1.5, $SVN::Depth::exclude is B<not> supported anywhere in
 the client-side (Wc/Client/etc) code; it is only supported as an argument to
 set_path functions in the Ra and Repos reporters.  (This will enable future
 versions of Subversion to run updates, etc, against 1.5 servers with proper

Propchange: subversion/branches/multi-wc-format/subversion/bindings/swig/python/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Fri Jan 14 14:01:45 2022
@@ -1,5 +1,6 @@
 *.pyc
 *.py
+__pycache__
 *.c
 *.la
 *.lo