We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Preprocess text widget should remove stop-words
<!--
This is an issue template. Please fill in the relevant details in the
sections below.
-->

##### Text version
<!-- From menu _Options→Add-ons→Orange3-Text_ or code `orangecontrib.text.version.full_version` -->
0.7.3dev

##### Orange version
<!-- From menu _Help→About→Version_ or code `Orange.version.full_version` -->
3.24.0dev

##### Expected behavior

- Preprocess text widget should remove stop-words from the text for the Slovene language.
- Stopwords languages should be ordered in alphabetical order.

##### Actual behavior

It does not happen when stopwords from NLTK are used - the problem is spaces beside the stopwords.

##### Steps to reproduce the behavior

Read some data with Import documents and connect with Preprocess Text widget. Observe the output of Preprocess Text widget.

##### Additional info (worksheets, data, screenshots, ...)




--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `orangecontrib/text/preprocess/filter.py`
Content:
```
1 import os
2 import re
3 
4 from Orange.data.io import detect_encoding
5 from gensim import corpora
6 from nltk.corpus import stopwords
7 
8 from orangecontrib.text.misc import wait_nltk_data
9 
10 __all__ = ['BaseTokenFilter', 'StopwordsFilter', 'LexiconFilter', 'RegexpFilter', 'FrequencyFilter']
11 
12 
13 class BaseTokenFilter:
14     name = NotImplemented
15 
16     def __call__(self, corpus):
17         if len(corpus) == 0:
18             return corpus
19         if isinstance(corpus[0], str):
20             return self.filter(corpus)
21         return [self.filter(tokens) for tokens in corpus]
22 
23     def filter(self, tokens):
24         return list(filter(self.check, tokens))
25 
26     def check(self, token):
27         raise NotImplementedError
28 
29     def __str__(self):
30         return self.name
31 
32     def set_up(self):
33         """ A method for setting filters up before every __call__. """
34         pass
35 
36     def tear_down(self):
37         """ A method for cleaning up after every __call__. """
38         pass
39 
40 
41 class WordListMixin:
42     def __init__(self, word_list=None):
43         self.file_path = None
44         self.word_list = word_list or []
45 
46     def from_file(self, path):
47         self.file_path = path
48         self.word_list = []
49         if not path:
50             return
51 
52         for encoding in ('utf-8',
53                          None,  # sys.getdefaultencoding()
54                          detect_encoding(path)):
55             try:
56                 with open(path, encoding=encoding) as f:
57                     self.word_list = set(line.strip() for line in f)
58             except UnicodeDecodeError:
59                 continue
60             return
61         # No encoding worked, raise
62         raise UnicodeError("Couldn't determine file encoding")
63 
64 
65 class StopwordsFilter(BaseTokenFilter, WordListMixin):
66     """ Remove tokens present in NLTK's language specific lists or a file. """
67     name = 'Stopwords'
68 
69     @staticmethod
70     @wait_nltk_data
71     def supported_languages():
72         # get NLTK list of stopwords
73         stopwords_listdir = []
74         try:
75             stopwords_listdir = [file for file in
76                                  os.listdir(stopwords._get_root())
77                                  if file.islower()]
78         except LookupError:  # when no NLTK data is available
79             pass
80 
81         return [file.capitalize() for file in stopwords_listdir]
82 
83     @wait_nltk_data
84     def __init__(self, language='English', word_list=None):
85         WordListMixin.__init__(self, word_list)
86         super().__init__()
87         self.language = language
88 
89     @property
90     def language(self):
91         return self._language
92 
93     @language.setter
94     def language(self, value):
95         self._language = value
96         if not self._language:
97             self.stopwords = []
98         else:
99             self.stopwords = set(stopwords.words(self.language.lower()))
100 
101     def __str__(self):
102         config = ''
103         config += 'Language: {}, '.format(self.language.capitalize()) if self.language else ''
104         config += 'File: {}, '.format(self.file_path) if self.file_path else ''
105         return '{} ({})'.format(self.name, config.strip(', '))
106 
107     def check(self, token):
108         return token not in self.stopwords and token not in self.word_list
109 
110 
111 class LexiconFilter(BaseTokenFilter, WordListMixin):
112     """ Keep only tokens present in a file. """
113     name = 'Lexicon'
114 
115     def __init__(self, lexicon=None):
116         WordListMixin.__init__(self, word_list=lexicon)
117 
118     @property
119     def lexicon(self):
120         return self.word_list
121 
122     @lexicon.setter
123     def lexicon(self, value):
124         self.word_list = set(value)
125 
126     def check(self, token):
127         return not self.lexicon or token in self.lexicon
128 
129     def __str__(self):
130         return '{} ({})'.format(self.name, 'File: {}'.format(self.file_path))
131 
132 
133 class RegexpFilter(BaseTokenFilter):
134     """ Remove tokens matching this regular expressions. """
135     name = 'Regexp'
136 
137     def __init__(self, pattern=r'\.|,|:|!|\?'):
138         self._pattern = pattern
139         # Compiled Regexes are NOT deepcopy-able and hence to make Corpus deepcopy-able
140         # we cannot store then (due to Corpus also storing used_preprocessor for BoW compute values).
141         # To bypass the problem regex is compiled before every __call__ and discarded right after.
142         self.regex = None
143         self.set_up()
144 
145     @property
146     def pattern(self):
147         return self._pattern
148 
149     @pattern.setter
150     def pattern(self, value):
151         self._pattern = value
152         self.set_up()
153 
154     @staticmethod
155     def validate_regexp(regexp):
156         try:
157             re.compile(regexp)
158             return True
159         except re.error:
160             return False
161 
162     def check(self, token):
163         return not self.regex.match(token)
164 
165     def __str__(self):
166         return '{} ({})'.format(self.name, self.pattern)
167 
168     def set_up(self):
169         """ Compile Regex before the __call__. """
170         self.regex = re.compile(self.pattern)
171 
172     def tear_down(self):
173         """ Delete Regex after every __call__. """
174         self.regex = None
175 
176 
177 class FrequencyFilter(LexiconFilter):
178     """Remove tokens with document frequency outside this range;
179     use either absolute or relative frequency. """
180     name = 'Document frequency'
181 
182     def __init__(self, min_df=0., max_df=1., keep_n=None):
183         super().__init__()
184         self._corpus_len = 0
185         self.keep_n = keep_n
186         self._max_df = max_df
187         self._min_df = min_df
188 
189     def fit_filter(self, corpus):
190         self._corpus_len = len(corpus)
191         tokens = getattr(corpus, 'tokens', corpus)
192         dictionary = corpora.Dictionary(tokens)
193         dictionary.filter_extremes(self.min_df, self.max_df, self.keep_n)
194         self.lexicon = dictionary.token2id.keys()
195         return self(tokens), dictionary
196 
197     @property
198     def max_df(self):
199         if isinstance(self._max_df, int):
200             return self._max_df / self._corpus_len if self._corpus_len else 1.
201         else:
202             return self._max_df
203 
204     @max_df.setter
205     def max_df(self, value):
206         self._max_df = value
207 
208     @property
209     def min_df(self):
210         if isinstance(self._min_df, float):
211             return int(self._corpus_len * self._min_df) or 1
212         else:
213             return self._min_df
214 
215     @min_df.setter
216     def min_df(self, value):
217         self._min_df = value
218 
219     def __str__(self):
220         keep = ', keep {}'.format(self.keep_n) if self.keep_n else ''
221         return "{} (range [{}, {}]{})".format(self.name, self._min_df,
222                                               self._max_df, keep)
223 
224     def check(self, token):
225         return token in self.lexicon
226
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/orangecontrib/text/preprocess/filter.py b/orangecontrib/text/preprocess/filter.py
--- a/orangecontrib/text/preprocess/filter.py
+++ b/orangecontrib/text/preprocess/filter.py
@@ -78,7 +78,7 @@
         except LookupError:  # when no NLTK data is available
             pass
 
-        return [file.capitalize() for file in stopwords_listdir]
+        return sorted(file.capitalize() for file in stopwords_listdir)
 
     @wait_nltk_data
     def __init__(self, language='English', word_list=None):
@@ -96,7 +96,8 @@
         if not self._language:
             self.stopwords = []
         else:
-            self.stopwords = set(stopwords.words(self.language.lower()))
+            self.stopwords = set(
+                x.strip() for x in stopwords.words(self.language.lower()))
 
     def __str__(self):
         config = ''

{"golden_diff": "diff --git a/orangecontrib/text/preprocess/filter.py b/orangecontrib/text/preprocess/filter.py\n--- a/orangecontrib/text/preprocess/filter.py\n+++ b/orangecontrib/text/preprocess/filter.py\n@@ -78,7 +78,7 @@\n         except LookupError:  # when no NLTK data is available\n             pass\n \n-        return [file.capitalize() for file in stopwords_listdir]\n+        return sorted(file.capitalize() for file in stopwords_listdir)\n \n     @wait_nltk_data\n     def __init__(self, language='English', word_list=None):\n@@ -96,7 +96,8 @@\n         if not self._language:\n             self.stopwords = []\n         else:\n-            self.stopwords = set(stopwords.words(self.language.lower()))\n+            self.stopwords = set(\n+                x.strip() for x in stopwords.words(self.language.lower()))\n \n     def __str__(self):\n         config = ''\n", "issue": "Preprocess text widget should remove stop-words\n<!--\r\nThis is an issue template. Please fill in the relevant details in the\r\nsections below.\r\n-->\r\n\r\n##### Text version\r\n<!-- From menu _Options\u2192Add-ons\u2192Orange3-Text_ or code `orangecontrib.text.version.full_version` -->\r\n0.7.3dev\r\n\r\n##### Orange version\r\n<!-- From menu _Help\u2192About\u2192Version_ or code `Orange.version.full_version` -->\r\n3.24.0dev\r\n\r\n##### Expected behavior\r\n\r\n- Preprocess text widget should remove stop-words from the text for the Slovene language.\r\n- Stopwords languages should be ordered in alphabetical order.\r\n\r\n##### Actual behavior\r\n\r\nIt does not happen when stopwords from NLTK are used - the problem is spaces beside the stopwords.\r\n\r\n##### Steps to reproduce the behavior\r\n\r\nRead some data with Import documents and connect with Preprocess Text widget. Observe the output of Preprocess Text widget.\r\n\r\n##### Additional info (worksheets, data, screenshots, ...)\r\n\r\n\r\n\n", "before_files": [{"content": "import os\nimport re\n\nfrom Orange.data.io import detect_encoding\nfrom gensim import corpora\nfrom nltk.corpus import stopwords\n\nfrom orangecontrib.text.misc import wait_nltk_data\n\n__all__ = ['BaseTokenFilter', 'StopwordsFilter', 'LexiconFilter', 'RegexpFilter', 'FrequencyFilter']\n\n\nclass BaseTokenFilter:\n    name = NotImplemented\n\n    def __call__(self, corpus):\n        if len(corpus) == 0:\n            return corpus\n        if isinstance(corpus[0], str):\n            return self.filter(corpus)\n        return [self.filter(tokens) for tokens in corpus]\n\n    def filter(self, tokens):\n        return list(filter(self.check, tokens))\n\n    def check(self, token):\n        raise NotImplementedError\n\n    def __str__(self):\n        return self.name\n\n    def set_up(self):\n        \"\"\" A method for setting filters up before every __call__. \"\"\"\n        pass\n\n    def tear_down(self):\n        \"\"\" A method for cleaning up after every __call__. \"\"\"\n        pass\n\n\nclass WordListMixin:\n    def __init__(self, word_list=None):\n        self.file_path = None\n        self.word_list = word_list or []\n\n    def from_file(self, path):\n        self.file_path = path\n        self.word_list = []\n        if not path:\n            return\n\n        for encoding in ('utf-8',\n                         None,  # sys.getdefaultencoding()\n                         detect_encoding(path)):\n            try:\n                with open(path, encoding=encoding) as f:\n                    self.word_list = set(line.strip() for line in f)\n            except UnicodeDecodeError:\n                continue\n            return\n        # No encoding worked, raise\n        raise UnicodeError(\"Couldn't determine file encoding\")\n\n\nclass StopwordsFilter(BaseTokenFilter, WordListMixin):\n    \"\"\" Remove tokens present in NLTK's language specific lists or a file. \"\"\"\n    name = 'Stopwords'\n\n    @staticmethod\n    @wait_nltk_data\n    def supported_languages():\n        # get NLTK list of stopwords\n        stopwords_listdir = []\n        try:\n            stopwords_listdir = [file for file in\n                                 os.listdir(stopwords._get_root())\n                                 if file.islower()]\n        except LookupError:  # when no NLTK data is available\n            pass\n\n        return [file.capitalize() for file in stopwords_listdir]\n\n    @wait_nltk_data\n    def __init__(self, language='English', word_list=None):\n        WordListMixin.__init__(self, word_list)\n        super().__init__()\n        self.language = language\n\n    @property\n    def language(self):\n        return self._language\n\n    @language.setter\n    def language(self, value):\n        self._language = value\n        if not self._language:\n            self.stopwords = []\n        else:\n            self.stopwords = set(stopwords.words(self.language.lower()))\n\n    def __str__(self):\n        config = ''\n        config += 'Language: {}, '.format(self.language.capitalize()) if self.language else ''\n        config += 'File: {}, '.format(self.file_path) if self.file_path else ''\n        return '{} ({})'.format(self.name, config.strip(', '))\n\n    def check(self, token):\n        return token not in self.stopwords and token not in self.word_list\n\n\nclass LexiconFilter(BaseTokenFilter, WordListMixin):\n    \"\"\" Keep only tokens present in a file. \"\"\"\n    name = 'Lexicon'\n\n    def __init__(self, lexicon=None):\n        WordListMixin.__init__(self, word_list=lexicon)\n\n    @property\n    def lexicon(self):\n        return self.word_list\n\n    @lexicon.setter\n    def lexicon(self, value):\n        self.word_list = set(value)\n\n    def check(self, token):\n        return not self.lexicon or token in self.lexicon\n\n    def __str__(self):\n        return '{} ({})'.format(self.name, 'File: {}'.format(self.file_path))\n\n\nclass RegexpFilter(BaseTokenFilter):\n    \"\"\" Remove tokens matching this regular expressions. \"\"\"\n    name = 'Regexp'\n\n    def __init__(self, pattern=r'\\.|,|:|!|\\?'):\n        self._pattern = pattern\n        # Compiled Regexes are NOT deepcopy-able and hence to make Corpus deepcopy-able\n        # we cannot store then (due to Corpus also storing used_preprocessor for BoW compute values).\n        # To bypass the problem regex is compiled before every __call__ and discarded right after.\n        self.regex = None\n        self.set_up()\n\n    @property\n    def pattern(self):\n        return self._pattern\n\n    @pattern.setter\n    def pattern(self, value):\n        self._pattern = value\n        self.set_up()\n\n    @staticmethod\n    def validate_regexp(regexp):\n        try:\n            re.compile(regexp)\n            return True\n        except re.error:\n            return False\n\n    def check(self, token):\n        return not self.regex.match(token)\n\n    def __str__(self):\n        return '{} ({})'.format(self.name, self.pattern)\n\n    def set_up(self):\n        \"\"\" Compile Regex before the __call__. \"\"\"\n        self.regex = re.compile(self.pattern)\n\n    def tear_down(self):\n        \"\"\" Delete Regex after every __call__. \"\"\"\n        self.regex = None\n\n\nclass FrequencyFilter(LexiconFilter):\n    \"\"\"Remove tokens with document frequency outside this range;\n    use either absolute or relative frequency. \"\"\"\n    name = 'Document frequency'\n\n    def __init__(self, min_df=0., max_df=1., keep_n=None):\n        super().__init__()\n        self._corpus_len = 0\n        self.keep_n = keep_n\n        self._max_df = max_df\n        self._min_df = min_df\n\n    def fit_filter(self, corpus):\n        self._corpus_len = len(corpus)\n        tokens = getattr(corpus, 'tokens', corpus)\n        dictionary = corpora.Dictionary(tokens)\n        dictionary.filter_extremes(self.min_df, self.max_df, self.keep_n)\n        self.lexicon = dictionary.token2id.keys()\n        return self(tokens), dictionary\n\n    @property\n    def max_df(self):\n        if isinstance(self._max_df, int):\n            return self._max_df / self._corpus_len if self._corpus_len else 1.\n        else:\n            return self._max_df\n\n    @max_df.setter\n    def max_df(self, value):\n        self._max_df = value\n\n    @property\n    def min_df(self):\n        if isinstance(self._min_df, float):\n            return int(self._corpus_len * self._min_df) or 1\n        else:\n            return self._min_df\n\n    @min_df.setter\n    def min_df(self, value):\n        self._min_df = value\n\n    def __str__(self):\n        keep = ', keep {}'.format(self.keep_n) if self.keep_n else ''\n        return \"{} (range [{}, {}]{})\".format(self.name, self._min_df,\n                                              self._max_df, keep)\n\n    def check(self, token):\n        return token in self.lexicon\n", "path": "orangecontrib/text/preprocess/filter.py"}], "after_files": [{"content": "import os\nimport re\n\nfrom Orange.data.io import detect_encoding\nfrom gensim import corpora\nfrom nltk.corpus import stopwords\n\nfrom orangecontrib.text.misc import wait_nltk_data\n\n__all__ = ['BaseTokenFilter', 'StopwordsFilter', 'LexiconFilter', 'RegexpFilter', 'FrequencyFilter']\n\n\nclass BaseTokenFilter:\n    name = NotImplemented\n\n    def __call__(self, corpus):\n        if len(corpus) == 0:\n            return corpus\n        if isinstance(corpus[0], str):\n            return self.filter(corpus)\n        return [self.filter(tokens) for tokens in corpus]\n\n    def filter(self, tokens):\n        return list(filter(self.check, tokens))\n\n    def check(self, token):\n        raise NotImplementedError\n\n    def __str__(self):\n        return self.name\n\n    def set_up(self):\n        \"\"\" A method for setting filters up before every __call__. \"\"\"\n        pass\n\n    def tear_down(self):\n        \"\"\" A method for cleaning up after every __call__. \"\"\"\n        pass\n\n\nclass WordListMixin:\n    def __init__(self, word_list=None):\n        self.file_path = None\n        self.word_list = word_list or []\n\n    def from_file(self, path):\n        self.file_path = path\n        self.word_list = []\n        if not path:\n            return\n\n        for encoding in ('utf-8',\n                         None,  # sys.getdefaultencoding()\n                         detect_encoding(path)):\n            try:\n                with open(path, encoding=encoding) as f:\n                    self.word_list = set(line.strip() for line in f)\n            except UnicodeDecodeError:\n                continue\n            return\n        # No encoding worked, raise\n        raise UnicodeError(\"Couldn't determine file encoding\")\n\n\nclass StopwordsFilter(BaseTokenFilter, WordListMixin):\n    \"\"\" Remove tokens present in NLTK's language specific lists or a file. \"\"\"\n    name = 'Stopwords'\n\n    @staticmethod\n    @wait_nltk_data\n    def supported_languages():\n        # get NLTK list of stopwords\n        stopwords_listdir = []\n        try:\n            stopwords_listdir = [file for file in\n                                 os.listdir(stopwords._get_root())\n                                 if file.islower()]\n        except LookupError:  # when no NLTK data is available\n            pass\n\n        return sorted(file.capitalize() for file in stopwords_listdir)\n\n    @wait_nltk_data\n    def __init__(self, language='English', word_list=None):\n        WordListMixin.__init__(self, word_list)\n        super().__init__()\n        self.language = language\n\n    @property\n    def language(self):\n        return self._language\n\n    @language.setter\n    def language(self, value):\n        self._language = value\n        if not self._language:\n            self.stopwords = []\n        else:\n            self.stopwords = set(\n                x.strip() for x in stopwords.words(self.language.lower()))\n\n    def __str__(self):\n        config = ''\n        config += 'Language: {}, '.format(self.language.capitalize()) if self.language else ''\n        config += 'File: {}, '.format(self.file_path) if self.file_path else ''\n        return '{} ({})'.format(self.name, config.strip(', '))\n\n    def check(self, token):\n        return token not in self.stopwords and token not in self.word_list\n\n\nclass LexiconFilter(BaseTokenFilter, WordListMixin):\n    \"\"\" Keep only tokens present in a file. \"\"\"\n    name = 'Lexicon'\n\n    def __init__(self, lexicon=None):\n        WordListMixin.__init__(self, word_list=lexicon)\n\n    @property\n    def lexicon(self):\n        return self.word_list\n\n    @lexicon.setter\n    def lexicon(self, value):\n        self.word_list = set(value)\n\n    def check(self, token):\n        return not self.lexicon or token in self.lexicon\n\n    def __str__(self):\n        return '{} ({})'.format(self.name, 'File: {}'.format(self.file_path))\n\n\nclass RegexpFilter(BaseTokenFilter):\n    \"\"\" Remove tokens matching this regular expressions. \"\"\"\n    name = 'Regexp'\n\n    def __init__(self, pattern=r'\\.|,|:|!|\\?'):\n        self._pattern = pattern\n        # Compiled Regexes are NOT deepcopy-able and hence to make Corpus deepcopy-able\n        # we cannot store then (due to Corpus also storing used_preprocessor for BoW compute values).\n        # To bypass the problem regex is compiled before every __call__ and discarded right after.\n        self.regex = None\n        self.set_up()\n\n    @property\n    def pattern(self):\n        return self._pattern\n\n    @pattern.setter\n    def pattern(self, value):\n        self._pattern = value\n        self.set_up()\n\n    @staticmethod\n    def validate_regexp(regexp):\n        try:\n            re.compile(regexp)\n            return True\n        except re.error:\n            return False\n\n    def check(self, token):\n        return not self.regex.match(token)\n\n    def __str__(self):\n        return '{} ({})'.format(self.name, self.pattern)\n\n    def set_up(self):\n        \"\"\" Compile Regex before the __call__. \"\"\"\n        self.regex = re.compile(self.pattern)\n\n    def tear_down(self):\n        \"\"\" Delete Regex after every __call__. \"\"\"\n        self.regex = None\n\n\nclass FrequencyFilter(LexiconFilter):\n    \"\"\"Remove tokens with document frequency outside this range;\n    use either absolute or relative frequency. \"\"\"\n    name = 'Document frequency'\n\n    def __init__(self, min_df=0., max_df=1., keep_n=None):\n        super().__init__()\n        self._corpus_len = 0\n        self.keep_n = keep_n\n        self._max_df = max_df\n        self._min_df = min_df\n\n    def fit_filter(self, corpus):\n        self._corpus_len = len(corpus)\n        tokens = getattr(corpus, 'tokens', corpus)\n        dictionary = corpora.Dictionary(tokens)\n        dictionary.filter_extremes(self.min_df, self.max_df, self.keep_n)\n        self.lexicon = dictionary.token2id.keys()\n        return self(tokens), dictionary\n\n    @property\n    def max_df(self):\n        if isinstance(self._max_df, int):\n            return self._max_df / self._corpus_len if self._corpus_len else 1.\n        else:\n            return self._max_df\n\n    @max_df.setter\n    def max_df(self, value):\n        self._max_df = value\n\n    @property\n    def min_df(self):\n        if isinstance(self._min_df, float):\n            return int(self._corpus_len * self._min_df) or 1\n        else:\n            return self._min_df\n\n    @min_df.setter\n    def min_df(self, value):\n        self._min_df = value\n\n    def __str__(self):\n        keep = ', keep {}'.format(self.keep_n) if self.keep_n else ''\n        return \"{} (range [{}, {}]{})\".format(self.name, self._min_df,\n                                              self._max_df, keep)\n\n    def check(self, token):\n        return token in self.lexicon\n", "path": "orangecontrib/text/preprocess/filter.py"}]}