Repository: doccano/doccano

Issue:
ModuleNotFoundError: No module named 'fcntl'
How to reproduce the behaviour
---------
After downloading doccano and trying to start it via `doccano init` I get the following message:

```
doccano init
Traceback (most recent call last):
  File "C:\Users\\AppData\Local\Programs\Python\Python39\Scripts\doccano-script.py", line 33, in <module>
    sys.exit(load_entry_point('doccano==1.4.1', 'console_scripts', 'doccano')())
  File "C:\Users\\AppData\Local\Programs\Python\Python39\Scripts\doccano-script.py", line 25, in importlib_load_entry_point
    return next(matches).load()
  File "c:\users\\appdata\local\programs\python\python39\lib\importlib\metadata.py", line 77, in load
    module = import_module(match.group('module'))
  File "c:\users\\appdata\local\programs\python\python39\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 790, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "c:\users\\appdata\local\programs\python\python39\lib\site-packages\backend\cli.py", line 7, in <module>
    import gunicorn.app.base
  File "c:\users\\appdata\local\programs\python\python39\lib\site-packages\gunicorn\app\base.py", line 11, in <module>
    from gunicorn import util
  File "c:\users\\appdata\local\programs\python\python39\lib\site-packages\gunicorn\util.py", line 8, in <module>
    import fcntl
ModuleNotFoundError: No module named 'fcntl' 
```

Your Environment
---------
*   Operating System: Windows 10 1909
*   Python Version Used: 3.9.4
*   When you install doccano: 17.06.2021
*   How did you install doccano (Heroku button etc): `pip install doccano`

Own Research:
----------
Apparently Windows doesn''t support `fcntl`. Therefore nobody that uses Windows can install doccano via pip.



Discussion:
I ran into the same issue and used docker to install Doccano on windows.
Yes with docker everything seems fine, but I think this should be stated somewhere.
The problem is gunicorn. It doesn't support Windows.
There's now a plan to add Windows support, but it may be faster to use some alternatives.
- https://github.com/benoitc/gunicorn/issues/524
Alternative: use waitress in cli.py.

```python
from waitress import serve
# import gunicorn.app.base
# import gunicorn.util

def command_run_webserver(args):
    sys.path.append(base)
    from app.wsgi import application
    serve(application, port=args.port)
```

Resolving patch:
diff --git a/backend/cli.py b/backend/cli.py
--- a/backend/cli.py
+++ b/backend/cli.py
@@ -1,15 +1,13 @@
 import argparse
 import multiprocessing
 import os
+import platform
 import subprocess
 import sys
 
-import gunicorn.app.base
-import gunicorn.util
-
 from .app.celery import app
-
 base = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(base)
 manage_path = os.path.join(base, 'manage.py')
 parser = argparse.ArgumentParser(description='doccano, text annotation for machine learning practitioners.')
 
@@ -18,21 +16,37 @@ def number_of_workers():
     return (multiprocessing.cpu_count() * 2) + 1
 
 
-class StandaloneApplication(gunicorn.app.base.BaseApplication):
+def run_on_nix(args):
+    import gunicorn.app.base
+    import gunicorn.util
+
+    class StandaloneApplication(gunicorn.app.base.BaseApplication):
 
-    def __init__(self, options=None):
-        self.options = options or {}
-        super().__init__()
+        def __init__(self, options=None):
+            self.options = options or {}
+            super().__init__()
 
-    def load_config(self):
-        config = {key: value for key, value in self.options.items()
-                  if key in self.cfg.settings and value is not None}
-        for key, value in config.items():
-            self.cfg.set(key.lower(), value)
+        def load_config(self):
+            config = {key: value for key, value in self.options.items()
+                      if key in self.cfg.settings and value is not None}
+            for key, value in config.items():
+                self.cfg.set(key.lower(), value)
 
-    def load(self):
-        sys.path.append(base)
-        return gunicorn.util.import_app('app.wsgi')
+        def load(self):
+            return gunicorn.util.import_app('app.wsgi')
+
+    options = {
+        'bind': '%s:%s' % ('0.0.0.0', args.port),
+        'workers': number_of_workers(),
+        'chdir': base
+    }
+    StandaloneApplication(options).run()
+
+
+def run_on_windows(args):
+    from waitress import serve
+    from app.wsgi import application
+    serve(application, port=args.port)
 
 
 def command_db_init(args):
@@ -53,12 +67,10 @@ def command_user_create(args):
 
 def command_run_webserver(args):
     print(f'Starting server with port {args.port}.')
-    options = {
-        'bind': '%s:%s' % ('0.0.0.0', args.port),
-        'workers': number_of_workers(),
-        'chdir': base
-    }
-    StandaloneApplication(options).run()
+    if platform.system() == 'Windows':
+        run_on_windows(args)
+    else:
+        run_on_nix(args)
 
 
 def command_run_task_queue(args):
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -42,6 +42,7 @@
     'django-drf-filepond>=0.3.0',
     'sqlalchemy>=1.4.7',
     'gunicorn>=20.1.0',
+    'waitress>=2.0.0',
 ]
 
 setup(


Test patch: