New Upstream Release - urlscan

Ready changes

Summary

Merged new upstream version: 0.9.10 (was: 0.9.9).

Resulting package

Built on 2023-05-23T17:39 (took 4m22s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases urlscan

Lintian Result

Diff

diff --git a/debian/changelog b/debian/changelog
index 8318633..ae218f1 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+urlscan (0.9.10-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Tue, 23 May 2023 17:35:37 -0000
+
 urlscan (0.9.9-1) unstable; urgency=medium
 
   * New upstream version 0.9.9 (Closes: #999338)
diff --git a/debian/patches/0001-Source-patch-removing-from-binary-package-not-needed.patch b/debian/patches/0001-Source-patch-removing-from-binary-package-not-needed.patch
index 9b5c138..29fb2e2 100644
--- a/debian/patches/0001-Source-patch-removing-from-binary-package-not-needed.patch
+++ b/debian/patches/0001-Source-patch-removing-from-binary-package-not-needed.patch
@@ -6,10 +6,10 @@ Subject: Source patch removing from binary package not needed COPYING file
  setup.py | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
 
-diff --git a/setup.py b/setup.py
-index 7951f9a..f6afd13 100755
---- a/setup.py
-+++ b/setup.py
+Index: urlscan.git/setup.py
+===================================================================
+--- urlscan.git.orig/setup.py
++++ urlscan.git/setup.py
 @@ -24,7 +24,7 @@ setup(name="urlscan",
            'console_scripts': ['urlscan=urlscan.__main__:main']
        },
diff --git a/setup.py b/setup.py
index 7951f9a..495ec88 100755
--- a/setup.py
+++ b/setup.py
@@ -11,14 +11,14 @@ def long_description():
 
 
 setup(name="urlscan",
-      version="0.9.9",
+      version="0.9.10",
       description="View/select the URLs in an email message or file",
       long_description=long_description(),
       long_description_content_type="text/markdown",
       author="Scott Hansen",
       author_email="firecat4153@gmail.com",
       url="https://github.com/firecat53/urlscan",
-      download_url="https://github.com/firecat53/urlscan/archive/0.9.9.zip",
+      download_url="https://github.com/firecat53/urlscan/archive/0.9.10.zip",
       packages=['urlscan'],
       entry_points={
           'console_scripts': ['urlscan=urlscan.__main__:main']
diff --git a/urlscan/urlchoose.py b/urlscan/urlchoose.py
index 83efdf8..a2d7a7f 100644
--- a/urlscan/urlchoose.py
+++ b/urlscan/urlchoose.py
@@ -18,6 +18,7 @@
 """An urwid listview-based widget that lets you choose a URL from a list of
 URLs."""
 
+import contextlib
 import json
 import os
 from os.path import dirname, exists, expanduser
@@ -353,7 +354,7 @@ class URLChooser:
     def _background_queue(self, mode):
         """Open URLs in background"""
         for url in self.queue:
-            self.mkbrowseto(url, thread=True, mode=mode)()
+            self.mkbrowseto(url, mode=mode)()
         self.draw_screen()
 
     def _queue(self, mode=2):
@@ -744,48 +745,41 @@ class URLChooser:
                 self.header.format(self.link_open_modes[0], len(self.queue))), 'header')
             self.top.base_widget.header = self.headerwid
 
-    def mkbrowseto(self, url, thread=False, mode=0):
+    def mkbrowseto(self, url, mode=0):
         """Create the urwid callback function to open the web browser or call
         another function with the URL.
 
         """
         def browse(*args):  # pylint: disable=unused-argument
-            # These 3 lines prevent any stderr messages from webbrowser or xdg
-            savout = os.dup(2)
-            os.close(2)
-            os.open(os.devnull, os.O_RDWR)
             # double ()() to ensure self.search evaluated at runtime, not when
             # browse() is _created_. [0] is self.search, [1] is self.enter
             # self.enter prevents opening URL when in search mode
-            if self._get_search()[0]() is True:
-                if self._get_search()[1]() is True:
-                    self.search = False
-                    self.enter = False
-            elif self.link_open_modes[0] == "Web Browser":
-                webbrowser.open(url, new=mode)
-            elif self.link_open_modes[0] == "Xdg-Open":
-                subprocess.run(shlex.split(f'xdg-open "{url}"'), check=False)
-            elif self.link_open_modes[0] == self.runsafe:
-                if self.pipe:
-                    subprocess.run(shlex.split(self.runsafe),
+            with redirect_output():
+                if self._get_search()[0]() is True:
+                    if self._get_search()[1]() is True:
+                        self.search = False
+                        self.enter = False
+                elif self.link_open_modes[0] == "Web Browser":
+                    webbrowser.open(url, new=mode)
+                elif self.link_open_modes[0] == "Xdg-Open":
+                    subprocess.run(shlex.split(f'xdg-open "{url}"'), check=False)
+                elif self.link_open_modes[0] == self.runsafe:
+                    if self.pipe:
+                        subprocess.run(shlex.split(self.runsafe),
+                                       check=False,
+                                       input=url.encode(sys.getdefaultencoding()))
+                    else:
+                        cmd = [i.format(url) for i in shlex.split(self.runsafe)]
+                        subprocess.run(cmd, check=False)
+                elif self.link_open_modes[0] == self.run and self.pipe:
+                    subprocess.run(shlex.split(self.run),
                                    check=False,
                                    input=url.encode(sys.getdefaultencoding()))
                 else:
-                    cmd = [i.format(url) for i in shlex.split(self.runsafe)]
-                    subprocess.run(cmd, check=False)
-            elif self.link_open_modes[0] == self.run and self.pipe:
-                subprocess.run(shlex.split(self.run),
-                               check=False,
-                               input=url.encode(sys.getdefaultencoding()))
-            else:
-                subprocess.run(self.run.format(url), check=False, shell=True)
+                    subprocess.run(self.run.format(url), check=False, shell=True)
 
-            if self.single is True:
-                self._quit()
-            # Restore normal stderr
-            os.dup2(savout, 2)
-            if thread is False:
-                self.draw_screen()
+                if self.single is True:
+                    self._quit()
         return browse
 
     def process_urls(self, extractedurls, dedupe, shorten):
@@ -820,6 +814,7 @@ class URLChooser:
                     if chunk.url is None:
                         markup.append(('msgtext', chunk.markup))
                     else:
+                        chunk.url = chunk.url.strip()
                         if (dedupe is True and chunk.url not in urls) \
                                 or dedupe is False:
                             urls.append(chunk.url)
@@ -830,7 +825,8 @@ class URLChooser:
                         if chunk.markup:
                             tmpmarkup.append(('msgtext', chunk.markup))
                         while i < len(chunks) and \
-                                chunks[i].url == chunk.url:
+                                (chunks[i].url if chunks[i].url is None
+                                    else chunks[i].url.strip()) == chunk.url:
                             if chunks[i].markup:
                                 tmpmarkup.append(chunks[i].markup)
                             i += 1
@@ -861,3 +857,29 @@ class URLChooser:
                 items.append(urwid.Columns(markup))
 
         return items, urls
+
+
+@contextlib.contextmanager
+def redirect_output():
+    """
+    A context manager to temporarily redirect stderr and stdout to devnull
+
+    Usage:
+        with redirect_output():
+            webbrowser.open('https://google.com')
+
+    """
+    try:
+        err = os.dup(sys.stderr.fileno())
+        out = os.dup(sys.stdout.fileno())
+        dest_file = open(os.devnull, 'w')
+        os.dup2(dest_file.fileno(), sys.stderr.fileno())
+        os.dup2(dest_file.fileno(), sys.stdout.fileno())
+        yield
+    finally:
+        if err is not None:
+            os.dup2(err, sys.stderr.fileno())
+        if out is not None:
+            os.dup2(out, sys.stdout.fileno())
+        if dest_file is not None:
+            dest_file.close()
diff --git a/urlscan/urlscan.py b/urlscan/urlscan.py
index d00ed36..659578b 100644
--- a/urlscan/urlscan.py
+++ b/urlscan/urlscan.py
@@ -245,7 +245,7 @@ class HTMLChunker(HTMLParser):
             self.handle_data(f"&{name};")
 
 
-URLINTERNALPATTERN = r'[{}()@\w/\\\-%?!&.=:;+,#~]'
+URLINTERNALPATTERN = r'[{}()@\w/\\\-%?!&.=:;+,#~*]'
 URLTRAILINGPATTERN = r'[{}(@\w/\-%&=+#$]'
 HTTPURLPATTERN = (r'(?:(https?|file|ftps?)://' + URLINTERNALPATTERN +
                   r'*' + URLTRAILINGPATTERN + r')')

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/urlscan-0.9.10.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/urlscan-0.9.10.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/urlscan-0.9.10.egg-info/entry_points.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/urlscan-0.9.10.egg-info/requires.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/urlscan-0.9.10.egg-info/top_level.txt

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/urlscan-0.9.9.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/urlscan-0.9.9.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/urlscan-0.9.9.egg-info/entry_points.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/urlscan-0.9.9.egg-info/requires.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/urlscan-0.9.9.egg-info/top_level.txt

No differences were encountered in the control files

More details

Full run details