forked from pool/python-pydot3
Accepting request 957383 from home:pgajdos:python
- work with python3 - added patches fix https://github.com/log0/pydot3/pull/5 + python-pydot3-python3.patch OBS-URL: https://build.opensuse.org/request/show/957383 OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-pydot3?expand=0&rev=7
This commit is contained in:
parent
caa1bdefa2
commit
9a997f1e89
461
python-pydot3-python3.patch
Normal file
461
python-pydot3-python3.patch
Normal file
@ -0,0 +1,461 @@
|
||||
From 499edc6dddc2fc7094685c29422943ca84013790 Mon Sep 17 00:00:00 2001
|
||||
From: yatin <ykarel@redhat.com>
|
||||
Date: Fri, 12 May 2017 15:30:12 +0530
|
||||
Subject: [PATCH] Make it Compatible with Python3
|
||||
|
||||
Some code syntax is not compatible with python3, this patch
|
||||
fixes makes it compatible for both python 2 and 3.
|
||||
Following changes are done:
|
||||
|
||||
- except Exception, e => except Exception as e
|
||||
- raise Error, "MSG" => raise Error("MSG")
|
||||
- print "MSG" => print("MSG")
|
||||
- use builtins.int to coverup long in python 2
|
||||
- use past.builtins.basestring to coverup basestring in python 3
|
||||
- d.itervalues() => iter(d.values())
|
||||
- d.iteritems() => iter(d.items())
|
||||
- d.iterkeys() => iter(d.keys())
|
||||
- using 'in' in place of 'has_key'
|
||||
---
|
||||
README.md | 1 +
|
||||
pydot/__init__.py | 87 +++++++++++++++++++++++----------------------
|
||||
pydot/dot_parser.py | 17 ++++-----
|
||||
3 files changed, 55 insertions(+), 50 deletions(-)
|
||||
|
||||
Index: pydot3-1.0.9/README.md
|
||||
===================================================================
|
||||
--- pydot3-1.0.9.orig/README.md 2016-01-24 05:57:48.000000000 +0100
|
||||
+++ pydot3-1.0.9/README.md 2022-02-24 15:25:47.605445798 +0100
|
||||
@@ -24,5 +24,6 @@ This code is distributed under the MIT l
|
||||
Prerequisites:
|
||||
--------------
|
||||
|
||||
+- future
|
||||
- pyparsing
|
||||
- GraphViz
|
||||
Index: pydot3-1.0.9/pydot/__init__.py
|
||||
===================================================================
|
||||
--- pydot3-1.0.9.orig/pydot/__init__.py 2016-11-27 00:48:12.000000000 +0100
|
||||
+++ pydot3-1.0.9/pydot/__init__.py 2022-02-24 15:25:47.605445798 +0100
|
||||
@@ -22,15 +22,18 @@ __author__ = 'Eric Chio'
|
||||
__version__ = '1.0.7'
|
||||
__license__ = 'MIT'
|
||||
|
||||
+from builtins import int
|
||||
+from past.builtins import basestring
|
||||
+
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
import copy
|
||||
try:
|
||||
- import dot_parser
|
||||
-except Exception, e:
|
||||
- print "Couldn't import dot_parser, loading of dot files will not be possible."
|
||||
+ from . import dot_parser
|
||||
+except Exception as e:
|
||||
+ print("Couldn't import dot_parser, loading of dot files will not be possible.")
|
||||
|
||||
GRAPH_ATTRIBUTES = set( ['Damping', 'K', 'URL', 'aspect', 'bb', 'bgcolor',
|
||||
'center', 'charset', 'clusterrank', 'colorscheme', 'comment', 'compound',
|
||||
@@ -90,7 +93,7 @@ CLUSTER_ATTRIBUTES = set( ['K', 'URL', '
|
||||
#
|
||||
class frozendict(dict):
|
||||
def _blocked_attribute(obj):
|
||||
- raise AttributeError, "A frozendict cannot be modified."
|
||||
+ raise AttributeError("A frozendict cannot be modified.")
|
||||
_blocked_attribute = property(_blocked_attribute)
|
||||
|
||||
__delitem__ = __setitem__ = clear = _blocked_attribute
|
||||
@@ -103,7 +106,7 @@ class frozendict(dict):
|
||||
for arg in args:
|
||||
if isinstance(arg, dict):
|
||||
arg = copy.copy(arg)
|
||||
- for k, v in arg.iteritems():
|
||||
+ for k, v in iter(arg.items()):
|
||||
if isinstance(v, frozendict):
|
||||
arg[k] = v
|
||||
elif isinstance(v, dict):
|
||||
@@ -130,7 +133,7 @@ class frozendict(dict):
|
||||
try:
|
||||
return self._cached_hash
|
||||
except AttributeError:
|
||||
- h = self._cached_hash = hash(tuple(sorted(self.iteritems())))
|
||||
+ h = self._cached_hash = hash(tuple(sorted(iter(self.items()))))
|
||||
return h
|
||||
|
||||
def __repr__(self):
|
||||
@@ -367,7 +370,7 @@ def __find_executables(path):
|
||||
|
||||
if os.path.isdir(path) :
|
||||
|
||||
- for prg in progs.iterkeys():
|
||||
+ for prg in iter(progs.keys()):
|
||||
|
||||
if progs[prg]:
|
||||
continue
|
||||
@@ -501,10 +504,10 @@ def find_graphviz():
|
||||
path = os.path.join(path, "bin")
|
||||
progs = __find_executables(path)
|
||||
if progs is not None :
|
||||
- #print "Used Windows registry"
|
||||
+ #print("Used Windows registry")
|
||||
return progs
|
||||
|
||||
- except Exception, excp:
|
||||
+ except Exception as excp:
|
||||
#raise excp
|
||||
pass
|
||||
else:
|
||||
@@ -514,12 +517,12 @@ def find_graphviz():
|
||||
|
||||
# Method 2 (Linux, Windows etc)
|
||||
#
|
||||
- if os.environ.has_key('PATH'):
|
||||
+ if 'PATH' in os.environ:
|
||||
|
||||
for path in os.environ['PATH'].split(os.pathsep):
|
||||
progs = __find_executables(path)
|
||||
if progs is not None :
|
||||
- #print "Used path"
|
||||
+ #print("Used path")
|
||||
return progs
|
||||
|
||||
# Method 3 (Windows only)
|
||||
@@ -530,7 +533,7 @@ def find_graphviz():
|
||||
# machine (might be on drive D:, or in a different language)
|
||||
#
|
||||
|
||||
- if os.environ.has_key('PROGRAMFILES'):
|
||||
+ if 'PROGRAMFILES' in os.environ:
|
||||
|
||||
# Note, we could also use the win32api to get this
|
||||
# information, but win32api may not be installed.
|
||||
@@ -546,7 +549,7 @@ def find_graphviz():
|
||||
|
||||
if progs is not None :
|
||||
|
||||
- #print "Used default install location"
|
||||
+ #print("Used default install location")
|
||||
return progs
|
||||
|
||||
|
||||
@@ -558,7 +561,7 @@ def find_graphviz():
|
||||
|
||||
progs = __find_executables(path)
|
||||
if progs is not None :
|
||||
- #print "Used path"
|
||||
+ #print("Used path")
|
||||
return progs
|
||||
|
||||
# Failed to find GraphViz
|
||||
@@ -759,7 +762,7 @@ class Node(Common):
|
||||
if idx > 0 and idx+1 < len(name):
|
||||
name, port = name[:idx], name[idx:]
|
||||
|
||||
- if isinstance(name, (long, int)):
|
||||
+ if isinstance(name, int):
|
||||
name = str(name)
|
||||
|
||||
self.obj_dict['name'] = quote_if_necessary( name )
|
||||
@@ -810,7 +813,7 @@ class Node(Common):
|
||||
|
||||
node_attr = list()
|
||||
|
||||
- for attr, value in self.obj_dict['attributes'].iteritems():
|
||||
+ for attr, value in iter(self.obj_dict['attributes'].items()):
|
||||
if value is not None:
|
||||
node_attr.append( '%s=%s' % (attr, quote_if_necessary(value) ) )
|
||||
else:
|
||||
@@ -923,7 +926,7 @@ class Edge(Common):
|
||||
"""
|
||||
|
||||
if not isinstance(edge, Edge):
|
||||
- raise Error, "Can't compare and edge to a non-edge object."
|
||||
+ raise Error("Can't compare and edge to a non-edge object.")
|
||||
|
||||
if self.get_parent_graph().get_top_graph_type() == 'graph':
|
||||
|
||||
@@ -981,7 +984,7 @@ class Edge(Common):
|
||||
|
||||
if isinstance(src, frozendict):
|
||||
edge = [ Subgraph(obj_dict=src).to_string() ]
|
||||
- elif isinstance(src, (int, long)):
|
||||
+ elif isinstance(src, int):
|
||||
edge = [ str(src) ]
|
||||
else:
|
||||
edge = [ src ]
|
||||
@@ -997,7 +1000,7 @@ class Edge(Common):
|
||||
|
||||
if isinstance(dst, frozendict):
|
||||
edge.append( Subgraph(obj_dict=dst).to_string() )
|
||||
- elif isinstance(dst, (int, long)):
|
||||
+ elif isinstance(dst, int):
|
||||
edge.append( str(dst) )
|
||||
else:
|
||||
edge.append( dst )
|
||||
@@ -1005,7 +1008,7 @@ class Edge(Common):
|
||||
|
||||
edge_attr = list()
|
||||
|
||||
- for attr, value in self.obj_dict['attributes'].iteritems():
|
||||
+ for attr, value in iter(self.obj_dict['attributes'].items()):
|
||||
|
||||
if value is not None:
|
||||
edge_attr.append( '%s=%s' % (attr, quote_if_necessary(value) ) )
|
||||
@@ -1073,7 +1076,7 @@ class Graph(Common):
|
||||
self.obj_dict['attributes'] = dict(attrs)
|
||||
|
||||
if graph_type not in ['graph', 'digraph']:
|
||||
- raise Error, 'Invalid type "%s". Accepted graph types are: graph, digraph, subgraph' % graph_type
|
||||
+ raise Error('Invalid type "%s". Accepted graph types are: graph, digraph, subgraph' % graph_type)
|
||||
|
||||
|
||||
self.obj_dict['name'] = quote_if_necessary(graph_name)
|
||||
@@ -1309,7 +1312,7 @@ class Graph(Common):
|
||||
if isinstance(name, Node):
|
||||
name = name.get_name()
|
||||
|
||||
- if self.obj_dict['nodes'].has_key(name):
|
||||
+ if name in self.obj_dict['nodes']:
|
||||
|
||||
if index is not None and index < len(self.obj_dict['nodes'][name]):
|
||||
del self.obj_dict['nodes'][name][index]
|
||||
@@ -1334,7 +1337,7 @@ class Graph(Common):
|
||||
|
||||
match = list()
|
||||
|
||||
- if self.obj_dict['nodes'].has_key(name):
|
||||
+ if name in self.obj_dict['nodes']:
|
||||
|
||||
match.extend( [ Node( obj_dict = obj_dict ) for obj_dict in self.obj_dict['nodes'][name] ])
|
||||
|
||||
@@ -1356,7 +1359,7 @@ class Graph(Common):
|
||||
|
||||
node_objs = list()
|
||||
|
||||
- for node, obj_dict_list in self.obj_dict['nodes'].iteritems():
|
||||
+ for node, obj_dict_list in iter(self.obj_dict['nodes'].items()):
|
||||
node_objs.extend( [ Node( obj_dict = obj_d ) for obj_d in obj_dict_list ] )
|
||||
|
||||
return node_objs
|
||||
@@ -1375,7 +1378,7 @@ class Graph(Common):
|
||||
|
||||
edge_points = ( graph_edge.get_source(), graph_edge.get_destination() )
|
||||
|
||||
- if self.obj_dict['edges'].has_key(edge_points):
|
||||
+ if edge_points in self.obj_dict['edges']:
|
||||
|
||||
edge_list = self.obj_dict['edges'][edge_points]
|
||||
edge_list.append(graph_edge.obj_dict)
|
||||
@@ -1409,7 +1412,7 @@ class Graph(Common):
|
||||
"""
|
||||
|
||||
if isinstance( src_or_list, (list, tuple)):
|
||||
- if dst is not None and isinstance(dst, (int, long)):
|
||||
+ if dst is not None and isinstance(dst, int):
|
||||
index = dst
|
||||
src, dst = src_or_list
|
||||
else:
|
||||
@@ -1421,7 +1424,7 @@ class Graph(Common):
|
||||
if isinstance(dst, Node):
|
||||
dst = dst.get_name()
|
||||
|
||||
- if self.obj_dict['edges'].has_key( (src, dst) ):
|
||||
+ if (src, dst) in self.obj_dict['edges']:
|
||||
|
||||
if index is not None and index < len(self.obj_dict['edges'][(src, dst)]):
|
||||
del self.obj_dict['edges'][(src, dst)][index]
|
||||
@@ -1453,8 +1456,8 @@ class Graph(Common):
|
||||
|
||||
match = list()
|
||||
|
||||
- if self.obj_dict['edges'].has_key( edge_points ) or (
|
||||
- self.get_top_graph_type() == 'graph' and self.obj_dict['edges'].has_key( edge_points_reverse )):
|
||||
+ if edge_points in self.obj_dict['edges'] or (
|
||||
+ self.get_top_graph_type() == 'graph' and edge_points_reverse in self.obj_dict['edges']):
|
||||
|
||||
edges_obj_dict = self.obj_dict['edges'].get(
|
||||
edge_points,
|
||||
@@ -1479,7 +1482,7 @@ class Graph(Common):
|
||||
|
||||
edge_objs = list()
|
||||
|
||||
- for edge, obj_dict_list in self.obj_dict['edges'].iteritems():
|
||||
+ for edge, obj_dict_list in iter(self.obj_dict['edges'].items()):
|
||||
edge_objs.extend( [ Edge( obj_dict = obj_d ) for obj_d in obj_dict_list ] )
|
||||
|
||||
return edge_objs
|
||||
@@ -1496,7 +1499,7 @@ class Graph(Common):
|
||||
if not isinstance(sgraph, Subgraph) and not isinstance(sgraph, Cluster):
|
||||
raise TypeError('add_subgraph() received a non subgraph class object:' + str(sgraph))
|
||||
|
||||
- if self.obj_dict['subgraphs'].has_key(sgraph.get_name()):
|
||||
+ if sgraph.get_name() in self.obj_dict['subgraphs']:
|
||||
|
||||
sgraph_list = self.obj_dict['subgraphs'][ sgraph.get_name() ]
|
||||
sgraph_list.append( sgraph.obj_dict )
|
||||
@@ -1524,7 +1527,7 @@ class Graph(Common):
|
||||
|
||||
match = list()
|
||||
|
||||
- if self.obj_dict['subgraphs'].has_key( name ):
|
||||
+ if name in self.obj_dict['subgraphs']:
|
||||
|
||||
sgraphs_obj_dict = self.obj_dict['subgraphs'].get( name )
|
||||
|
||||
@@ -1549,7 +1552,7 @@ class Graph(Common):
|
||||
|
||||
sgraph_objs = list()
|
||||
|
||||
- for sgraph, obj_dict_list in self.obj_dict['subgraphs'].iteritems():
|
||||
+ for sgraph, obj_dict_list in iter(self.obj_dict['subgraphs'].items()):
|
||||
sgraph_objs.extend( [ Subgraph( obj_dict = obj_d ) for obj_d in obj_dict_list ] )
|
||||
|
||||
return sgraph_objs
|
||||
@@ -1560,15 +1563,15 @@ class Graph(Common):
|
||||
|
||||
self.obj_dict['parent_graph'] = parent_graph
|
||||
|
||||
- for obj_list in self.obj_dict['nodes'].itervalues():
|
||||
+ for obj_list in iter(self.obj_dict['nodes'].values()):
|
||||
for obj in obj_list:
|
||||
obj['parent_graph'] = parent_graph
|
||||
|
||||
- for obj_list in self.obj_dict['edges'].itervalues():
|
||||
+ for obj_list in iter(self.obj_dict['edges'].values()):
|
||||
for obj in obj_list:
|
||||
obj['parent_graph'] = parent_graph
|
||||
|
||||
- for obj_list in self.obj_dict['subgraphs'].itervalues():
|
||||
+ for obj_list in iter(self.obj_dict['subgraphs'].values()):
|
||||
for obj in obj_list:
|
||||
Graph(obj_dict=obj).set_parent_graph(parent_graph)
|
||||
|
||||
@@ -1598,7 +1601,7 @@ class Graph(Common):
|
||||
graph.append( '%s %s {\n' % (self.obj_dict['type'], self.obj_dict['name']) )
|
||||
|
||||
|
||||
- for attr in self.obj_dict['attributes'].iterkeys():
|
||||
+ for attr in iter(self.obj_dict['attributes'].keys()):
|
||||
|
||||
if self.obj_dict['attributes'].get(attr, None) is not None:
|
||||
|
||||
@@ -1614,7 +1617,7 @@ class Graph(Common):
|
||||
edges_done = set()
|
||||
|
||||
edge_obj_dicts = list()
|
||||
- for e in self.obj_dict['edges'].itervalues():
|
||||
+ for e in iter(self.obj_dict['edges'].values()):
|
||||
edge_obj_dicts.extend(e)
|
||||
|
||||
if edge_obj_dicts:
|
||||
@@ -1624,11 +1627,11 @@ class Graph(Common):
|
||||
edge_src_set, edge_dst_set = set(), set()
|
||||
|
||||
node_obj_dicts = list()
|
||||
- for e in self.obj_dict['nodes'].itervalues():
|
||||
+ for e in iter(self.obj_dict['nodes'].values()):
|
||||
node_obj_dicts.extend(e)
|
||||
|
||||
sgraph_obj_dicts = list()
|
||||
- for sg in self.obj_dict['subgraphs'].itervalues():
|
||||
+ for sg in iter(self.obj_dict['subgraphs'].values()):
|
||||
sgraph_obj_dicts.extend(sg)
|
||||
|
||||
|
||||
@@ -1950,7 +1953,7 @@ class Dot(Graph):
|
||||
raise InvocationException(
|
||||
'GraphViz\'s executables not found' )
|
||||
|
||||
- if not self.progs.has_key(prog):
|
||||
+ if prog not in self.progs:
|
||||
raise InvocationException(
|
||||
'GraphViz\'s executable "%s" not found' % prog )
|
||||
|
||||
@@ -2020,7 +2023,7 @@ class Dot(Graph):
|
||||
'Program terminated with status: %d. stderr follows: %s' % (
|
||||
status, stderr_output) )
|
||||
elif stderr_output:
|
||||
- print stderr_output
|
||||
+ print(stderr_output)
|
||||
|
||||
# For each of the image files...
|
||||
#
|
||||
Index: pydot3-1.0.9/pydot/dot_parser.py
|
||||
===================================================================
|
||||
--- pydot3-1.0.9.orig/pydot/dot_parser.py 2016-11-27 00:48:12.000000000 +0100
|
||||
+++ pydot3-1.0.9/pydot/dot_parser.py 2022-02-24 15:25:47.605445798 +0100
|
||||
@@ -14,6 +14,7 @@ Ported by: Eric Chio <ckieric@gmail.com>
|
||||
__author__ = ['Michael Krause', 'Ero Carrera', 'Eric Chio']
|
||||
__license__ = 'MIT'
|
||||
|
||||
+from past.builtins import basestring
|
||||
|
||||
import sys
|
||||
import glob
|
||||
@@ -113,7 +114,7 @@ def push_top_graph_stmt(str, loc, toks):
|
||||
add_elements(g, element)
|
||||
|
||||
else:
|
||||
- raise ValueError, "Unknown element statement: %r " % element
|
||||
+ raise ValueError("Unknown element statement: %r " % element)
|
||||
|
||||
|
||||
for g in top_graphs:
|
||||
@@ -220,14 +221,14 @@ def add_elements(g, toks, defaults_graph
|
||||
defaults_edge.update(element.attrs)
|
||||
|
||||
else:
|
||||
- raise ValueError, "Unknown DefaultStatement: %s " % element.default_type
|
||||
+ raise ValueError("Unknown DefaultStatement: %s " % element.default_type)
|
||||
|
||||
elif isinstance(element, P_AttrList):
|
||||
|
||||
g.obj_dict['attributes'].update(element.attrs)
|
||||
|
||||
else:
|
||||
- raise ValueError, "Unknown element statement: %r" % element
|
||||
+ raise ValueError("Unknown element statement: %r" % element)
|
||||
|
||||
|
||||
def push_graph_stmt(str, loc, toks):
|
||||
@@ -269,7 +270,7 @@ def push_default_stmt(str, loc, toks):
|
||||
if default_type in ['graph', 'node', 'edge']:
|
||||
return DefaultStatement(default_type, attrs)
|
||||
else:
|
||||
- raise ValueError, "Unknown default statement: %r " % toks
|
||||
+ raise ValueError("Unknown default statement: %r " % toks)
|
||||
|
||||
|
||||
def push_attr_list(str, loc, toks):
|
||||
@@ -525,9 +526,9 @@ def parse_dot_data(data):
|
||||
else:
|
||||
return [g for g in tokens]
|
||||
|
||||
- except ParseException, err:
|
||||
+ except ParseException as err:
|
||||
|
||||
- print err.line
|
||||
- print " "*(err.column-1) + "^"
|
||||
- print err
|
||||
+ print(err.line)
|
||||
+ print(" "*(err.column-1) + "^")
|
||||
+ print(err)
|
||||
return None
|
||||
Index: pydot3-1.0.9/setup.py
|
||||
===================================================================
|
||||
--- pydot3-1.0.9.orig/setup.py 2016-11-27 00:51:22.000000000 +0100
|
||||
+++ pydot3-1.0.9/setup.py 2022-02-24 15:26:03.385540915 +0100
|
||||
@@ -41,10 +41,4 @@ config = dict(
|
||||
install_requires = ['pyparsing', 'setuptools'],
|
||||
data_files = [('.', ['LICENSE', 'README.md'])])
|
||||
|
||||
-
|
||||
-if sys.version_info >= (3,):
|
||||
- config.update(dict(
|
||||
- use_2to3=True,
|
||||
- ))
|
||||
-
|
||||
setup(**config)
|
@ -1,3 +1,11 @@
|
||||
-------------------------------------------------------------------
|
||||
Thu Feb 24 14:32:51 UTC 2022 - pgajdos@suse.com
|
||||
|
||||
- work with python3
|
||||
- added patches
|
||||
fix https://github.com/log0/pydot3/pull/5
|
||||
+ python-pydot3-python3.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Jun 29 15:17:34 UTC 2017 - jmatejek@suse.com
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# spec file for package python-pydot3
|
||||
# spec file
|
||||
#
|
||||
# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||
# Copyright (c) 2022 SUSE LLC
|
||||
# Copyright (c) 2017 Dr. Axel Braun
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
@ -13,7 +13,7 @@
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
# Please submit bugfixes or comments via http://bugs.opensuse.org/
|
||||
# Please submit bugfixes or comments via https://bugs.opensuse.org/
|
||||
#
|
||||
|
||||
|
||||
@ -26,10 +26,12 @@ Release: 0
|
||||
Summary: Create (dot) graphs from python
|
||||
License: MIT
|
||||
Group: Development/Libraries/Python
|
||||
Url: https://pypi.python.org/pypi/pydot3
|
||||
URL: https://pypi.python.org/pypi/pydot3
|
||||
Source: https://files.pythonhosted.org/packages/source/p/%{modname}/%{modname}-%{version}.tar.gz
|
||||
Source1: example-demo.py
|
||||
Source2: example-rank.py
|
||||
# https://github.com/log0/pydot3/pull/5
|
||||
Patch0: python-pydot3-python3.patch
|
||||
BuildRequires: %{python_module setuptools}
|
||||
BuildRequires: python-rpm-macros
|
||||
Requires: graphviz
|
||||
@ -43,6 +45,7 @@ Currently all attributes implemented in the Dot language are supported (up to Gr
|
||||
|
||||
%prep
|
||||
%setup -q -n %{modname}-%{version}
|
||||
%patch0 -p1
|
||||
|
||||
mkdir examples && cp %{SOURCE1} %{SOURCE2} examples
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user