How to install holidays library in Python

I’m trying to add the holidays library to one of my Python rules and have been unsuccessful so far. I’m running a standard openhabian installation on a RPi3 with openhab 2.5.8.

When I run

pip install holidays

at the command line it says it installs correctly but adding the line

import holidays

at the start of my rule doesn’t succeed and I see the following in my logs;

2020-11-13 09:48:16.785 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab2/automation/jsr223/python/personal/time_of_day.py': ImportError: No module named holidays in <script> at line number 4

A bit of hunting through the forums here revealed that there are some user and directory issues that need to be incorporated into the pip installation process, so I tried the following;

sudo -u openhab pip install --ignore-installed --install-option="--prefix=$OPENHAB_CONF/automation/lib/python/personal" holidays

but this failed. The output from this is as follows;

/usr/lib/python2.7/dist-packages/pip/_internal/commands/install.py:222: UserWarning: Disabling all use of wheels due to the use of --build-options / --global-options / --install-options.
  cmdoptions.check_install_build_global(options)
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting holidays
  Using cached https://files.pythonhosted.org/packages/d9/c7/1d0cb8f69bb4caa252a0c95de66a34e0548cbf9d3f4a2353cba5ee5f566c/holidays-0.10.3.tar.gz
Collecting convertdate (from holidays)
  Using cached https://files.pythonhosted.org/packages/da/57/2d01124f7122665d864ce510315fb1e458635bb65dfb4741fff3227fd7c5/convertdate-2.2.2.tar.gz
Collecting korean_lunar_calendar (from holidays)
  Using cached https://files.pythonhosted.org/packages/fa/c4/2f0f2329098ee24f629c3ad4dcb210fbedb52e51fa6348ebdcbd7af2151b/korean_lunar_calendar-0.2.1.tar.gz
Collecting python-dateutil (from holidays)
  Using cached https://files.pythonhosted.org/packages/be/ed/5bbc91f03fa4c839c4c7360375da77f9659af5f7086b7a7bdda65771c8e0/python-dateutil-2.8.1.tar.gz
  Installing build dependencies ... error
  Complete output from command /usr/bin/python -m pip install --ignore-installed --no-user --prefix /tmp/pip-build-env-yBGSFg --no-warn-script-location --no-binary :all: --only-binary :none: -i https://pypi.org/simple --extra-index-url https://www.piwheels.org/simple -- "setuptools; python_version != '3.3'" "setuptools<40.0; python_version == '3.3'" wheel setuptools_scm:
  Ignoring setuptools: markers 'python_version == "3.3"' don't match your environment
  Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple, https://www.piwheels.org/simple
  Collecting setuptools
    Using cached https://files.pythonhosted.org/packages/b2/40/4e00501c204b457f10fe410da0c97537214b2265247bc9a5bc6edd55b9e4/setuptools-44.1.1.zip
  Collecting wheel
    Using cached https://files.pythonhosted.org/packages/83/72/611c121b6bd15479cb62f1a425b2e3372e121b324228df28e64cc28b01c2/wheel-0.35.1.tar.gz
      Complete output from command python setup.py egg_info:
      Traceback (most recent call last):
        File "<string>", line 1, in <module>
        File "/tmp/pip-install-iXHpqb/wheel/setup.py", line 4, in <module>
          setup(maintainer=u'Alex Grönholm')
        File "/usr/lib/python2.7/dist-packages/setuptools/__init__.py", line 144, in setup
          _install_setup_requires(attrs)
        File "/usr/lib/python2.7/dist-packages/setuptools/__init__.py", line 139, in _install_setup_requires
          dist.fetch_build_eggs(dist.setup_requires)
        File "/usr/lib/python2.7/dist-packages/setuptools/dist.py", line 724, in fetch_build_eggs
          replace_conflicting=True,
        File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 782, in resolve
          replace_conflicting=replace_conflicting
        File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 1065, in best_match
          return self.obtain(req, installer)
        File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 1077, in obtain
          return installer(requirement)
        File "/usr/lib/python2.7/dist-packages/setuptools/dist.py", line 791, in fetch_build_egg
          return cmd.easy_install(req)
        File "/usr/lib/python2.7/dist-packages/setuptools/command/easy_install.py", line 704, in easy_install
          return self.install_item(spec, dist.location, tmpdir, deps)
        File "/usr/lib/python2.7/dist-packages/setuptools/command/easy_install.py", line 730, in install_item
          dists = self.install_eggs(spec, download, tmpdir)
        File "/usr/lib/python2.7/dist-packages/setuptools/command/easy_install.py", line 915, in install_eggs
          return self.build_and_install(setup_script, setup_base)
        File "/usr/lib/python2.7/dist-packages/setuptools/command/easy_install.py", line 1183, in build_and_install
          self.run_setup(setup_script, setup_base, args)
        File "/usr/lib/python2.7/dist-packages/setuptools/command/easy_install.py", line 1169, in run_setup
          run_setup(setup_script, args)
        File "/usr/lib/python2.7/dist-packages/setuptools/sandbox.py", line 233, in run_setup
          with setup_context(setup_dir):
        File "/usr/lib/python2.7/contextlib.py", line 17, in __enter__
          return self.gen.next()
        File "/usr/lib/python2.7/dist-packages/setuptools/sandbox.py", line 195, in setup_context
          yield
        File "/usr/lib/python2.7/contextlib.py", line 35, in __exit__
          self.gen.throw(type, value, traceback)
        File "/usr/lib/python2.7/dist-packages/setuptools/sandbox.py", line 166, in save_modules
          saved_exc.resume()
        File "/usr/lib/python2.7/dist-packages/setuptools/sandbox.py", line 141, in resume
          six.reraise(type, exc, self._tb)
        File "/usr/lib/python2.7/dist-packages/setuptools/sandbox.py", line 154, in save_modules
          yield saved
        File "/usr/lib/python2.7/dist-packages/setuptools/sandbox.py", line 194, in setup_context
          __import__('setuptools')
        File "setuptools/__init__.py", line 16, in <module>
        File "setuptools/version.py", line 1, in <module>
        File "pkg_resources/__init__.py", line 1365
          raise SyntaxError(e) from e
                                  ^
      SyntaxError: invalid syntax
  
      ----------------------------------------
  Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-iXHpqb/wheel/
  
  ----------------------------------------
Command "/usr/bin/python -m pip install --ignore-installed --no-user --prefix /tmp/pip-build-env-yBGSFg --no-warn-script-location --no-binary :all: --only-binary :none: -i https://pypi.org/simple --extra-index-url https://www.piwheels.org/simple -- "setuptools; python_version != '3.3'" "setuptools<40.0; python_version == '3.3'" wheel setuptools_scm" failed with error code 1 in None

I’m still pretty new to Python, but is this telling me that this library is written for Python 3 and therefore I won’t be able to install it? Is there any sort of workaround here? Why did it not fail when I ran the initial simple ‘pip’ command?

PyPi says it supports 2.7. You have the files and dependencies already installed, so you could manually copy them over. Or add your python installation directory to the python.path property in EXTRA_JAVA_OPTS.

As a general rule I like to encourage the use of openHAB features where possible. You are writing rules for openHAB after all so you will often end up with a better result if you use the features provided by openHAB instead of using native Python versions of things. Given that, why not use Ephemeris? It uses JollyDay so all you need to do is configure you coutnry and region and it will automatically configure your holidays. If that isn’t sufficient you can define your own. Then you can use the ephemeris actions in your rules. No need to import an external Python library.

OK, I hadn’t come across Ephemeris. It looks like it might do the job, although looking at the XML file for Australia it seems to have very few of our public holidays defined compared with the holidays library. Defining the rest probably isn’t too hard. Will I be able to define ‘the day before the last Saturday in September’?

Yes. See Actions | openHAB. The only ones that are tricky and require changes to code (i.e. we can’t handle that) are those holidays based on the lunar calendar like Easter, Rosh Hashanah, or Ramadan and the like. Those are directly programmed into the library. For everything else though you can define stuff like that.