Here, I ran into this problem.
File "c:\python37\Lib\runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "c:\python37\Lib\runpy.py", line 85, in _run_code
exec(code, run_globals)
File "C:\Users\***\Envs\CertBot-Virtual\Scripts\certbot.exe\__main__.py", line 5, in <module>
File "c:\users\***\envs\certbot-virtual\lib\site-packages\certbot\main.py", line 18, in <module>
from certbot import account
File "c:\users\***\envs\certbot-virtual\lib\site-packages\certbot\account.py", line 23, in <module>
from certbot import util
File "c:\users\***\envs\certbot-virtual\lib\site-packages\certbot\util.py", line 26, in <module>
from certbot import lock
File "c:\users\***\envs\certbot-virtual\lib\site-packages\certbot\lock.py", line 3, in <module>
import fcntl
ModuleNotFoundError: No module named 'fcntl'
After searching for awhile, I found a fix.
Steps to fix
pip install portalocker
.- Replace everything inside
lock.py
with:
import errno
import portalocker
import logging
import os
from certbot import errors
logger = logging.getLogger(__name__)
# This file has been edited as fcntl doesn't exist on windows, and several errors occur.
def lock_dir(dir_path):
"""Place a lock file on the directory at dir_path.
The lock file is placed in the root of dir_path with the name
.certbot.lock.
:param str dir_path: path to directory
:returns: the locked LockFile object
:rtype: LockFile
:raises errors.LockError: if unable to acquire the lock
"""
return LockFile(os.path.join(dir_path, '.certbot.lock'))
class LockFile(object):
"""A UNIX lock file.
This lock file is released when the locked file is closed or the
process exits. It cannot be used to provide synchronization between
threads. It is based on the lock_file package by Martin Horcicka.
"""
def __init__(self, path):
"""Initialize and acquire the lock file.
:param str path: path to the file to lock
:raises errors.LockError: if unable to acquire the lock
"""
super(LockFile, self).__init__()
self._path = path
self._file = None
self.acquire()
def acquire(self):
"""Acquire the lock file.
:raises errors.LockError: if lock is already held
:raises OSError: if unable to open or stat the lock file
"""
while self._file is None:
# Open the file
file = open(self._path, 'a+')
try:
self._try_lock(file)
if self._lock_success(file):
self._file = file
finally:
# Close the file if it is not the required one
if self._file is None:
os.close(file)
def _try_lock(self, file):
"""Try to acquire the lock file without blocking.
:param int file: file descriptor of the opened file to lock
"""
try:
portalocker.lock(file, portalocker.LOCK_EX | portalocker.LOCK_NB)
except IOError as err:
if err.errno in (errno.EACCES, errno.EAGAIN):
logger.debug(
"A lock on %s is held by another process.", self._path)
raise errors.LockError(
"Another instance of Certbot is already running.")
raise
def _lock_success(self, file):
"""Did we successfully grab the lock?
Because this class deletes the locked file when the lock is
released, it is possible another process removed and recreated
the file between us opening the file and acquiring the lock.
:param int file: file descriptor of the opened file to lock
:returns: True if the lock was successfully acquired
:rtype: bool
"""
try:
stat1 = os.stat(self._path)
except OSError as err:
if err.errno == errno.ENOENT:
return False
raise
stat2 = os.stat(file.name)
# If our locked file descriptor and the file on disk refer to
# the same device and inode, they're the same file.
return stat1.st_dev == stat2.st_dev and stat1.st_ino == stat2.st_ino
def __repr__(self):
repr_str = '{0}({1}) <'.format(self.__class__.__name__, self._path)
if self._file is None:
repr_str += 'released>'
else:
repr_str += 'acquired>'
return repr_str
def release(self):
"""Remove, close, and release the lock file."""
# It is important the lock file is removed before it's released,
# otherwise:
#
# process A: open lock file
# process B: release lock file
# process A: lock file
# process A: check device and inode
# process B: delete file
# process C: open and lock a different file at the same path
#
# Calling os.remove on a file that's in use doesn't work on
# Windows, but neither does locking with fcntl.
try:
self._file.close()
os.remove(self._path)
finally:
try:
self._file.close()
finally:
self._file = None
- You are done! Now you should get an AttributeError message (upon running
certbot
) as described in the tutorial, and should be able to follow the tutorial & fix it. There may be some more errors, but they should be easy to fix if you know Python a bit.
Tested on Python 3.7, Windows 10 (1803 Update).
Hope this helps someone!