diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index f5fa41c8..e699e97f 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -2,12 +2,12 @@ name: CMake on linux on: push: branches: - - master - - release-* - - v* + - master + - release-* + - v* pull_request: branches: - - master + - master jobs: Ubuntu_Latest_Apt: @@ -15,56 +15,56 @@ jobs: strategy: matrix: build_type: - - Debug - - Release - - RelNoOpenMP - - RelWithDebInfo + - Debug + - Release + - RelNoOpenMP + - RelWithDebInfo steps: - - name: checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 + - name: checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 - - name: install dependencies - run: |- - sudo apt-get update - sudo apt-get install -y cmake wget ninja-build libblas-dev liblapack-dev liblapacke-dev libboost-all-dev libopenmpi-dev libeigen3-dev libhdf5-dev - sudo apt-get clean all + - name: install dependencies + run: |- + sudo apt-get update + sudo apt-get install -y cmake wget ninja-build libblas-dev liblapack-dev liblapacke-dev libboost-all-dev libopenmpi-dev libeigen3-dev libhdf5-dev + sudo apt-get clean all - - name: Install Catch v3 - run: | - cd /tmp - wget -O Catch2.tar.gz https://github.com/catchorg/Catch2/archive/refs/tags/v3.6.0.tar.gz - tar xzf Catch2.tar.gz - rm Catch2.tar.gz - cd Catch2* - cmake -S . -B build -DBUILD_TESTING=OFF - cmake --build build - sudo cmake --install build + - name: Install Catch v3 + run: | + cd /tmp + wget -O Catch2.tar.gz https://github.com/catchorg/Catch2/archive/refs/tags/v3.6.0.tar.gz + tar xzf Catch2.tar.gz + rm Catch2.tar.gz + cd Catch2* + cmake -S . -B build -DBUILD_TESTING=OFF + cmake --build build + sudo cmake --install build - - name: Install HighFive - run: | - cd /tmp - wget -O HighFive.tar.gz https://github.com/BlueBrain/HighFive/archive/v2.2.2.tar.gz - tar xzf HighFive.tar.gz - rm HighFive.tar.gz - cd HighFive* - cmake -DHIGHFIVE_EXAMPLES=OFF -DHIGHFIVE_USE_BOOST=OFF -DHIGHFIVE_UNIT_TESTS=OFF -S . -B build - cmake --build build - sudo cmake --install build + - name: Install HighFive + run: | + cd /tmp + wget -O HighFive.tar.gz https://github.com/BlueBrain/HighFive/archive/v2.10.1.tar.gz + tar xzf HighFive.tar.gz + rm HighFive.tar.gz + cd HighFive* + cmake -DHIGHFIVE_EXAMPLES=OFF -DHIGHFIVE_USE_BOOST=OFF -DHIGHFIVE_UNIT_TESTS=OFF -S . -B build + cmake --build build + sudo cmake --install build - - name: Run CMake - run: |- - cmake -S . -B build.${{ matrix.build_type }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DENABLE_PYTHON=OFF + - name: Run CMake + run: |- + cmake -S . -B build.${{ matrix.build_type }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DENABLE_PYTHON=OFF - - name: Build - run: |- - cmake --build build.${{ matrix.build_type }} + - name: Build + run: |- + cmake --build build.${{ matrix.build_type }} - - name: Install - run: |- - sudo cmake --install build.${{ matrix.build_type }} + - name: Install + run: |- + sudo cmake --install build.${{ matrix.build_type }} - - name: Run tests - run: |- - smurff --bist + - name: Run tests + run: |- + smurff --bist diff --git a/.github/workflows/conda.yml b/.github/workflows/conda.yml index 7fa42916..ae2fab1e 100644 --- a/.github/workflows/conda.yml +++ b/.github/workflows/conda.yml @@ -11,16 +11,33 @@ on: jobs: conda-build: - name: Build Conda Package on ${{ matrix.os }} for Python ${{ matrix.python }} + name: Build Conda Package on ${{ matrix.os }} for Python ${{ matrix.pyver }} runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-13, macos-14] - python: [ "3.9", "3.10", "3.11", "3.12", "3.13" ] + pyver: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + include: + - os: windows-latest + cibw_archs: "native" + - os: macos-13 + cibw_archs: "native" + - os: macos-14 + cibw_archs: "native" + - os: ubuntu-latest + cibw_archs: "native" + - os: ubuntu-latest + cibw_archs: "aarch64" + defaults: run: shell: bash -el {0} + steps: + - name: Set up QEMU + if: matrix.cibw_archs == 'aarch64' + uses: docker/setup-qemu-action@v2 + with: + platforms: arm64 - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -31,16 +48,16 @@ jobs: python-version: 3.11 environment-file: conda-recipes/devtools.yml show-channel-urls: true - conda-remove-defaults: 'true' + conda-remove-defaults: "true" - name: Build conda packages run: | - conda build --python ${{ matrix.python }} --output-folder ../conda_packages smurff + conda build --python ${{ matrix.pyver }} --output-folder ../conda_packages smurff working-directory: conda-recipes - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: conda-${{ matrix.os }}-${{ matrix.python }} - path: conda_packages/* + name: conda-${{ matrix.os }}-${{ matrix.pyver }} + path: conda_packages/* merge: name: Merge all Conda artifacts @@ -50,4 +67,4 @@ jobs: - name: Merge Artifacts uses: actions/upload-artifact/merge@v4 with: - name: all-conda-packages \ No newline at end of file + name: all-conda-packages diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 43f5b1ab..8aff6a5b 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -11,30 +11,30 @@ on: jobs: build_wheels: - name: wheel ${{ matrix.pyver }}-${{ matrix.os }} + name: Build wheel ${{ matrix.pyver }}-${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-13, macos-14] - pyver: [cp39, cp310, cp311, cp312, cp313] + os: [ubuntu-latest, windows-latest, macos-14-large, macos-15-intel] + pyver: [cp39, cp310, cp311, cp312, cp313] steps: - uses: actions/checkout@v4 with: - submodules: 'true' + submodules: "true" fetch-depth: 0 # Used to host cibuildwheel - uses: actions/setup-python@v5 with: - python-version: '3.x' + python-version: "3.x" - name: Install cibuildwheel run: python -m pip install cibuildwheel - name: Build wheels env: - CIBW_BUILD: ${{matrix.pyver}}-* + CIBW_BUILD: ${{ matrix.pyver }}-* run: python -m cibuildwheel --output-dir wheelhouse - uses: actions/upload-artifact@v4 @@ -42,7 +42,6 @@ jobs: name: cibw-wheels-${{ matrix.os }}-${{ matrix.pyver }} path: ./wheelhouse/*.whl - merge: name: merge all wheel artifacts runs-on: ubuntu-latest @@ -51,4 +50,4 @@ jobs: - name: Merge Artifacts uses: actions/upload-artifact/merge@v4 with: - name: all-wheels \ No newline at end of file + name: all-wheels diff --git a/ci/docker/Dockerfile.alpine b/ci/docker/Dockerfile.alpine index d0dfef9a..360d9c0b 100644 --- a/ci/docker/Dockerfile.alpine +++ b/ci/docker/Dockerfile.alpine @@ -3,18 +3,18 @@ FROM alpine:3.19 RUN apk add \ binutils gcc g++ gfortran \ cmake ninja make \ - wget curl util-linux tar \ + wget curl util-linux tar \ git \ eigen-dev openblas-dev hdf5-dev boost-dev catch2-3 \ py3-pybind11-dev python3-dev py3-pip py3-pytest py3-parameterized py3-pytest-xdist #install HighFive -RUN wget -O HighFive.tar.gz https://github.com/BlueBrain/HighFive/archive/v2.2.2.tar.gz && \ - tar xzf HighFive.tar.gz && \ - rm HighFive.tar.gz && \ - cd HighFive* && \ - cmake -S . -B build -GNinja .. -DHIGHFIVE_USE_BOOST=OFF && \ - cmake --build build && \ - cmake --install build && \ - cd ../.. && \ - rm -r HighFive* +RUN wget -O HighFive.tar.gz https://github.com/BlueBrain/HighFive/archive/v2.10.1.tar.gz && \ + tar xzf HighFive.tar.gz && \ + rm HighFive.tar.gz && \ + cd HighFive* && \ + cmake -S . -B build -GNinja .. -DHIGHFIVE_USE_BOOST=OFF && \ + cmake --build build && \ + cmake --install build && \ + cd ../.. && \ + rm -r HighFive* diff --git a/ci/docker/Dockerfile.manylinux b/ci/docker/Dockerfile.manylinux index 49bca224..4cfc93f7 100644 --- a/ci/docker/Dockerfile.manylinux +++ b/ci/docker/Dockerfile.manylinux @@ -4,14 +4,14 @@ RUN yum -y install wget eigen3-devel openblas-devel hdf5-devel && \ yum clean all #install HighFive -RUN wget -O HighFive.tar.gz https://github.com/BlueBrain/HighFive/archive/v2.2.2.tar.gz && \ - tar xzf HighFive.tar.gz && \ - rm HighFive.tar.gz && \ - cd HighFive* && \ - mkdir build && \ - cd build && \ - cmake .. -DHIGHFIVE_USE_BOOST=OFF && \ - make -j2 && \ - make install && \ - cd ../.. && \ - rm -r HighFive* +RUN wget -O HighFive.tar.gz https://github.com/BlueBrain/HighFive/archive/v2.10.1.tar.gz && \ + tar xzf HighFive.tar.gz && \ + rm HighFive.tar.gz && \ + cd HighFive* && \ + mkdir build && \ + cd build && \ + cmake .. -DHIGHFIVE_USE_BOOST=OFF && \ + make -j2 && \ + make install && \ + cd ../.. && \ + rm -r HighFive* diff --git a/ci/docker/Dockerfile.musllinux b/ci/docker/Dockerfile.musllinux index 5ab15ec4..520597dc 100644 --- a/ci/docker/Dockerfile.musllinux +++ b/ci/docker/Dockerfile.musllinux @@ -3,7 +3,7 @@ FROM quay.io/pypa/musllinux_1_2_x86_64 RUN apk add wget eigen-dev openblas-dev hdf5-dev #install HighFive -RUN wget -O HighFive.tar.gz https://github.com/BlueBrain/HighFive/archive/v2.2.2.tar.gz && \ +RUN wget -O HighFive.tar.gz https://github.com/BlueBrain/HighFive/archive/v2.10.1.tar.gz && \ tar xzf HighFive.tar.gz && \ rm HighFive.tar.gz && \ cd HighFive* && \ diff --git a/ci/docker/Dockerfile.ubuntu b/ci/docker/Dockerfile.ubuntu index ece0b1a5..56bd856c 100644 --- a/ci/docker/Dockerfile.ubuntu +++ b/ci/docker/Dockerfile.ubuntu @@ -1,47 +1,47 @@ FROM ubuntu:22.04 RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - build-essential \ - cmake \ - git g++ python3 \ - libblas-dev \ - liblapack-dev \ - liblapacke-dev \ - libopenmpi-dev \ - openmpi-bin \ - libeigen3-dev \ - libboost-all-dev \ - ca-certificates \ - libhdf5-dev \ - gdb vim wget\ - libhdf5-dev \ - python3-pip \ - python3-numpy python3-pybind11 python3-setuptools \ - python3-scipy python3-pandas \ - python3-joblib python3-sklearn \ - python3-h5py \ - python3-pytest \ - python3-parameterized \ - python3-pytest-xdist \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + git g++ python3 \ + libblas-dev \ + liblapack-dev \ + liblapacke-dev \ + libopenmpi-dev \ + openmpi-bin \ + libeigen3-dev \ + libboost-all-dev \ + ca-certificates \ + libhdf5-dev \ + gdb vim wget\ + libhdf5-dev \ + python3-pip \ + python3-numpy python3-pybind11 python3-setuptools \ + python3-scipy python3-pandas \ + python3-joblib python3-sklearn \ + python3-h5py \ + python3-pytest \ + python3-parameterized \ + python3-pytest-xdist \ && rm -rf /var/lib/apt/lists/* # Install Catch v3 RUN cd /tmp && \ - wget -O Catch2.tar.gz https://github.com/catchorg/Catch2/archive/refs/tags/v3.6.0.tar.gz && \ - tar xzf Catch2.tar.gz && \ - rm Catch2.tar.gz && \ - cd Catch2* && \ - cmake -S . -B build -DBUILD_TESTING=OFF && \ - cmake --build build && \ - cmake --install build && \ - cd .. && \ - rm -r Catch* + wget -O Catch2.tar.gz https://github.com/catchorg/Catch2/archive/refs/tags/v3.6.0.tar.gz && \ + tar xzf Catch2.tar.gz && \ + rm Catch2.tar.gz && \ + cd Catch2* && \ + cmake -S . -B build -DBUILD_TESTING=OFF && \ + cmake --build build && \ + cmake --install build && \ + cd .. && \ + rm -r Catch* #install HighFive RUN cd /tmp && \ - wget -O HighFive.tar.gz https://github.com/BlueBrain/HighFive/archive/v2.2.2.tar.gz && \ + wget -O HighFive.tar.gz https://github.com/BlueBrain/HighFive/archive/v2.10.1.tar.gz && \ tar xzf HighFive.tar.gz && \ rm HighFive.tar.gz && \ cd HighFive* && \ diff --git a/ci/highfive.rb b/ci/highfive.rb index 7e859f4c..106f449c 100644 --- a/ci/highfive.rb +++ b/ci/highfive.rb @@ -4,7 +4,7 @@ class Highfive < Formula desc "HighFive - Header-only C++ HDF5 interface" homepage "https://bluebrain.github.io/HighFive/" - url "https://github.com/BlueBrain/HighFive/archive/v2.2.2.tar.gz" + url "https://github.com/BlueBrain/HighFive/archive/v2.10.1.tar.gz" sha256 "5bfb356705c6feb9d46a0507573028b289083ec4b4607a6f36187cb916f085a7" depends_on "cmake" => :build diff --git a/cmake/DependenciesConfig.cmake b/cmake/DependenciesConfig.cmake index 1e773231..3bac0f5a 100644 --- a/cmake/DependenciesConfig.cmake +++ b/cmake/DependenciesConfig.cmake @@ -116,7 +116,7 @@ endmacro(configure_highfive) macro(configure_boost) message ("Dependency check for boost...") if(${ENABLE_BOOST}) - FIND_PACKAGE(Boost COMPONENTS system program_options filesystem REQUIRED) + FIND_PACKAGE(Boost COMPONENTS program_options filesystem REQUIRED) message("-- Found Boost_VERSION: ${Boost_VERSION}") message("-- Found Boost_INCLUDE_DIRS: ${Boost_INCLUDE_DIRS}") diff --git a/conda-recipes/conda_build_config.yaml b/conda-recipes/conda_build_config.yaml index ae614f6b..5fc5410f 100644 --- a/conda-recipes/conda_build_config.yaml +++ b/conda-recipes/conda_build_config.yaml @@ -4,18 +4,19 @@ python: - 3.11 - 3.12 - 3.13 + - 3.14 -blas_impl: # [linux64 or win64] - - openblas # [linux64 or win64] - - mkl # [linux64 or win64] +blas_impl: # [linux64 or win64] + - openblas # [linux64 or win64] + - mkl # [linux64 or win64] hdf5: - 1.10.4 - 1.10.6 -c_compiler: vs2022 # [win] -cxx_compiler: vs2022 # [win] +c_compiler: vs2022 # [win] +cxx_compiler: vs2022 # [win] pin_run_as_build: - libboost: x.x - hdf5: x.x.x + libboost: x.x + hdf5: x.x.x diff --git a/conda-recipes/smurff/meta.yaml b/conda-recipes/smurff/meta.yaml index ed03092f..fff068ac 100644 --- a/conda-recipes/smurff/meta.yaml +++ b/conda-recipes/smurff/meta.yaml @@ -25,7 +25,7 @@ requirements: - catch2 - libboost-devel - pybind11 - - highfive >=2.2 + - highfive ==2.* - h5py - python {{ python }} - scikit-build-core diff --git a/python/test/test_macau.py b/python/test/test_macau.py index bb99c8d6..0224bde6 100644 --- a/python/test/test_macau.py +++ b/python/test/test_macau.py @@ -9,29 +9,31 @@ verbose = 0 -class TestMacau(unittest.TestCase): +class TestMacau(unittest.TestCase): # Python 2.7 @unittest.skip fix __name__ = "TestMacau" def test_macau(self): - Ydense = np.random.rand(10, 20) - r = np.random.permutation(10*20)[:40] # 40 random samples from 10*20 matrix - side1 = Ydense[:,1:2] - side2 = Ydense[1:2,:].transpose() - Y = scipy.sparse.coo_matrix(Ydense) # convert to sparse - Y = scipy.sparse.coo_matrix( (Y.data[r], (Y.row[r], Y.col[r])), shape=Y.shape ) + Ydense = np.random.rand(10, 20) + r = np.random.permutation(10 * 20)[:40] # 40 random samples from 10*20 matrix + side1 = Ydense[:, 1:2] + side2 = Ydense[1:2, :].transpose() + Y = scipy.sparse.coo_matrix(Ydense) # convert to sparse + Y = scipy.sparse.coo_matrix((Y.data[r], (Y.row[r], Y.col[r])), shape=Y.shape) Y, Ytest = smurff.make_train_test(Y, 0.5) - predictions = smurff.macau(Y, - Ytest=Ytest, - side_info=[side1, side2], - direct=True, - num_latent=4, - verbose=verbose, - num_threads=1, - burnin=200, - nsamples=200) + predictions = smurff.macau( + Y, + Ytest=Ytest, + side_info=[side1, side2], + direct=True, + num_latent=4, + verbose=verbose, + num_threads=1, + burnin=200, + nsamples=200, + ) self.assertEqual(Ytest.nnz, len(predictions)) @@ -40,47 +42,51 @@ def test_macau_side_bin(self): Xt = scipy.sparse.rand(15, 10, 0.1) F = scipy.sparse.rand(15, 2, 0.5) F.data[:] = 1 - smurff.macau(X, - Ytest=Xt, - side_info=[F, None], - direct=True, - num_latent=5, - burnin=200, - nsamples=200, - verbose=verbose, - num_threads=1, - ) + smurff.macau( + X, + Ytest=Xt, + side_info=[F, None], + direct=True, + num_latent=5, + burnin=200, + nsamples=200, + verbose=verbose, + num_threads=1, + ) def test_macau_dense(self): - Y = scipy.sparse.rand(15, 10, 0.2) + Y = scipy.sparse.rand(15, 10, 0.2) Yt = scipy.sparse.rand(15, 10, 0.1) - F = np.random.randn(15, 2) - smurff.macau(Y, - Ytest=Yt, - side_info=[F, None], - direct=True, - num_latent=5, - burnin=200, - nsamples=200, - verbose=verbose, - num_threads=1 - ) + F = np.random.randn(15, 2) + smurff.macau( + Y, + Ytest=Yt, + side_info=[F, None], + direct=True, + num_latent=5, + burnin=200, + nsamples=200, + verbose=verbose, + num_threads=1, + ) def test_macau_univariate(self): Y = scipy.sparse.rand(10, 20, 0.2) Y, Ytest = smurff.make_train_test(Y, 0.5) - side1 = scipy.sparse.coo_matrix( np.random.rand(10, 2) ) - side2 = scipy.sparse.coo_matrix( np.random.rand(20, 3) ) - - predictions = smurff.macau(Y, - Ytest=Ytest, - side_info=[side1, side2], - univariate = True, - num_latent=4, - verbose=verbose, - num_threads=1, - burnin=200, - nsamples=200) + side1 = scipy.sparse.coo_matrix(np.random.rand(10, 2)) + side2 = scipy.sparse.coo_matrix(np.random.rand(20, 3)) + + predictions = smurff.macau( + Y, + Ytest=Ytest, + side_info=[side1, side2], + univariate=True, + num_latent=4, + verbose=verbose, + num_threads=1, + burnin=200, + nsamples=200, + ) self.assertEqual(Ytest.nnz, len(predictions)) def test_macau_tensor(self): @@ -90,60 +96,68 @@ def test_macau_tensor(self): B = np.random.randn(shape[1], 2) C = np.random.randn(shape[2], 2) - idx = list( itertools.product(np.arange(shape[0]), np.arange(shape[1]), np.arange(shape[2])) ) - df = pd.DataFrame( np.asarray(idx), columns=["A", "B", "C"]) - df["value"] = np.array([ np.sum(A[i[0], :] * B[i[1], :] * C[i[2], :]) for i in idx ]) - Ytrain, Ytest = smurff.make_train_test(df, 0.2, shape = shape) + idx = list(itertools.product(np.arange(shape[0]), np.arange(shape[1]), np.arange(shape[2]))) + df = pd.DataFrame(np.asarray(idx), columns=["A", "B", "C"]) + df["value"] = np.array([np.sum(A[i[0], :] * B[i[1], :] * C[i[2], :]) for i in idx]) + Ytrain, Ytest = smurff.make_train_test(df, 0.2, shape=shape) Acoo = scipy.sparse.coo_matrix(A) - predictions = smurff.macau(Ytrain = Ytrain, - Ytest = Ytest, - side_info=[Acoo, None, None], - direct=True, - num_latent = 4, - verbose=verbose, - num_threads=1, - burnin=200, - nsamples=200) + predictions = smurff.macau( + Ytrain=Ytrain, + Ytest=Ytest, + side_info=[Acoo, None, None], + direct=True, + num_latent=4, + verbose=verbose, + num_threads=1, + burnin=200, + nsamples=200, + ) rmse = smurff.calc_rmse(predictions) - self.assertTrue(rmse < 1., - msg="Tensor factorization gave RMSE above 1. (%f)." % rmse) + self.assertTrue( + rmse < 1.1, + msg="Tensor factorization gave RMSE above 1.1 (%f)." % rmse, + ) def test_macau_tensor_univariate(self): - shape = [30, 4, 2] A = np.random.randn(shape[0], 2) B = np.random.randn(shape[1], 2) C = np.random.randn(shape[2], 2) - idx = list( itertools.product(np.arange(shape[0]), np.arange(shape[1]), np.arange(shape[2])) ) - df = pd.DataFrame( np.asarray(idx), columns=["A", "B", "C"]) - df["value"] = np.array([ np.sum(A[i[0], :] * B[i[1], :] * C[i[2], :]) for i in idx ]) + idx = list(itertools.product(np.arange(shape[0]), np.arange(shape[1]), np.arange(shape[2]))) + df = pd.DataFrame(np.asarray(idx), columns=["A", "B", "C"]) + df["value"] = np.array([np.sum(A[i[0], :] * B[i[1], :] * C[i[2], :]) for i in idx]) Ytrain, Ytest = smurff.make_train_test(df, 0.2, shape) Acoo = scipy.sparse.coo_matrix(A) - predictions = smurff.macau(Ytrain, - Ytest=Ytest, - side_info=[Acoo, None, None], - univariate = True, - num_latent=4, - verbose=verbose, - num_threads=1, - burnin=200, - nsamples=2000) + predictions = smurff.macau( + Ytrain, + Ytest=Ytest, + side_info=[Acoo, None, None], + univariate=True, + num_latent=4, + verbose=verbose, + num_threads=1, + burnin=200, + nsamples=2000, + ) rmse = smurff.calc_rmse(predictions) - self.assertTrue(rmse < 1., - msg="Tensor factorization gave RMSE above 1. (%f)." % rmse) + self.assertTrue( + rmse < 1.1, + msg="Tensor factorization gave RMSE above 1.1 (%f)." % rmse, + ) + -if __name__ == '__main__': +if __name__ == "__main__": for arg in sys.argv: - if (arg == "-v" or arg == "--verbose"): + if arg == "-v" or arg == "--verbose": verbose = 1 unittest.main()